mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 09:52:50 +08:00
commit
57d7b9dfde
@ -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
|
||||
|
@ -2,6 +2,9 @@
|
||||
|
||||
[](https://ci.appveyor.com/project/TomPallister/ocelot-fcfpb)
|
||||
|
||||
[](https://ci.appveyor.com/project/TomPallister/ocelot-fcfpb/history?branch=develop)
|
||||
|
||||
|
||||
[](https://coveralls.io/github/TomPallister/Ocelot?branch=develop)
|
||||
|
||||
Ocelot is a .NET Api Gateway. This project is aimed at people using .NET running
|
||||
|
@ -421,6 +421,8 @@ private void PublishPackages(ConvertableDirectoryPath packagesDir, ConvertableFi
|
||||
var codePackage = packagesDir + File(artifacts["nuget"]);
|
||||
|
||||
Information("Pushing package " + codePackage);
|
||||
|
||||
Information("Calling NuGetPush");
|
||||
|
||||
NuGetPush(
|
||||
codePackage,
|
||||
|
11
build.ps1
11
build.ps1
@ -158,14 +158,15 @@ if(-Not $SkipToolPackageRestore.IsPresent) {
|
||||
if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or
|
||||
($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) {
|
||||
Write-Verbose -Message "Missing or changed package.config hash..."
|
||||
Remove-Item * -Recurse -Exclude packages.config,nuget.exe
|
||||
Get-ChildItem -Exclude packages.config,nuget.exe,Cake.Bakery |
|
||||
Remove-Item -Recurse
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Restoring tools from NuGet..."
|
||||
$NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`""
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Throw "An error occured while restoring NuGet tools."
|
||||
Throw "An error occurred while restoring NuGet tools."
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -185,7 +186,7 @@ if (Test-Path $ADDINS_PACKAGES_CONFIG) {
|
||||
$NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$ADDINS_DIR`""
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Throw "An error occured while restoring NuGet addins."
|
||||
Throw "An error occurred while restoring NuGet addins."
|
||||
}
|
||||
|
||||
Write-Verbose -Message ($NuGetOutput | out-string)
|
||||
@ -202,7 +203,7 @@ if (Test-Path $MODULES_PACKAGES_CONFIG) {
|
||||
$NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$MODULES_DIR`""
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Throw "An error occured while restoring NuGet modules."
|
||||
Throw "An error occurred while restoring NuGet modules."
|
||||
}
|
||||
|
||||
Write-Verbose -Message ($NuGetOutput | out-string)
|
||||
@ -231,4 +232,4 @@ $cakeArguments += $ScriptArgs
|
||||
# Start Cake
|
||||
Write-Host "Running build script..."
|
||||
&$CAKE_EXE $cakeArguments
|
||||
exit $LASTEXITCODE
|
||||
exit $LASTEXITCODE
|
@ -61,7 +61,8 @@ Here is an example ReRoute configuration, You don't need to set all of these thi
|
||||
},
|
||||
"HttpHandlerOptions": {
|
||||
"AllowAutoRedirect": true,
|
||||
"UseCookieContainer": true
|
||||
"UseCookieContainer": true,
|
||||
"UseTracing": true
|
||||
},
|
||||
"UseServiceDiscovery": false
|
||||
}
|
||||
|
43
docs/features/delegatinghandlers.rst
Normal file
43
docs/features/delegatinghandlers.rst
Normal file
@ -0,0 +1,43 @@
|
||||
Delegating Handers
|
||||
==================
|
||||
|
||||
Ocelot allows the user to add delegating handlers to the HttpClient transport. This feature was requested `GitHub #208 <https://github.com/TomPallister/Ocelot/issues/208>`_ and I decided that it was going to be useful in various ways.
|
||||
|
||||
Usage
|
||||
^^^^^^
|
||||
|
||||
In order to add delegating handlers to the HttpClient transport you need to do the following.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
services.AddOcelot()
|
||||
.AddDelegatingHandler(() => new FakeHandler())
|
||||
.AddDelegatingHandler(() => new FakeHandler());
|
||||
|
||||
Or for singleton like behaviour..
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
var handlerOne = new FakeHandler();
|
||||
var handlerTwo = new FakeHandler();
|
||||
|
||||
services.AddOcelot()
|
||||
.AddDelegatingHandler(() => handlerOne)
|
||||
.AddDelegatingHandler(() => handlerTwo);
|
||||
|
||||
You can have as many DelegatingHandlers as you want and they are run in a first in first out order. If you are using Ocelot's QoS functionality then that will always be run after your last delegating handler.
|
||||
|
||||
In order to create a class that can be used a delegating handler it must look as follows
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
public class FakeHandler : DelegatingHandler
|
||||
{
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
//do stuff and optionally call the base handler..
|
||||
return await base.SendAsync(request, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
Hopefully other people will find this feature useful!
|
31
docs/features/tracing.rst
Normal file
31
docs/features/tracing.rst
Normal file
@ -0,0 +1,31 @@
|
||||
Tracing
|
||||
=======
|
||||
|
||||
Ocelot providers tracing functionality from the excellent `Butterfly <https://github.com/ButterflyAPM>`_ project.
|
||||
|
||||
In order to use the tracing please read the Butterfly documentation.
|
||||
|
||||
In ocelot you need to do the following if you wish to trace a ReRoute.
|
||||
|
||||
In your ConfigureServices method
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
services
|
||||
.AddOcelot(Configuration)
|
||||
.AddOpenTracing(option =>
|
||||
{
|
||||
//this is the url that the butterfly collector server is running on...
|
||||
option.CollectorUrl = "http://localhost:9618";
|
||||
option.Service = "Ocelot";
|
||||
});
|
||||
|
||||
Then in your configuration.json add the following to the ReRoute you want to trace..
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
"HttpHandlerOptions": {
|
||||
"UseTracing": true
|
||||
},
|
||||
|
||||
Ocelot will now send tracing information to Butterfly when this ReRoute is called.
|
@ -30,9 +30,12 @@ Thanks for taking a look at the Ocelot documentation. Please use the left hand n
|
||||
features/headerstransformation
|
||||
features/claimstransformation
|
||||
features/logging
|
||||
features/tracing
|
||||
features/requestid
|
||||
features/middlewareinjection
|
||||
features/loadbalancer
|
||||
features/delegatinghandlers
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"projects": [ "src", "test" ],
|
||||
"sdk": {
|
||||
"version": "2.0.2"
|
||||
"version": "2.1.4"
|
||||
}
|
||||
}
|
@ -6,13 +6,16 @@ namespace Ocelot.Cache
|
||||
public class CachedResponse
|
||||
{
|
||||
public CachedResponse(
|
||||
HttpStatusCode statusCode = HttpStatusCode.OK,
|
||||
Dictionary<string, IEnumerable<string>> headers = null,
|
||||
string body = null
|
||||
HttpStatusCode statusCode,
|
||||
Dictionary<string, IEnumerable<string>> headers,
|
||||
string body,
|
||||
Dictionary<string, IEnumerable<string>> contentHeaders
|
||||
|
||||
)
|
||||
{
|
||||
StatusCode = statusCode;
|
||||
Headers = headers ?? new Dictionary<string, IEnumerable<string>>();
|
||||
ContentHeaders = contentHeaders ?? new Dictionary<string, IEnumerable<string>>();
|
||||
Body = body ?? "";
|
||||
}
|
||||
|
||||
@ -20,6 +23,8 @@ namespace Ocelot.Cache
|
||||
|
||||
public Dictionary<string, IEnumerable<string>> Headers { get; private set; }
|
||||
|
||||
public Dictionary<string, IEnumerable<string>> ContentHeaders { get; private set; }
|
||||
|
||||
public string Body { get; private set; }
|
||||
}
|
||||
}
|
||||
|
@ -82,13 +82,21 @@ namespace Ocelot.Cache.Middleware
|
||||
}
|
||||
|
||||
var response = new HttpResponseMessage(cached.StatusCode);
|
||||
|
||||
foreach (var header in cached.Headers)
|
||||
{
|
||||
response.Headers.Add(header.Key, header.Value);
|
||||
}
|
||||
|
||||
var content = new MemoryStream(Convert.FromBase64String(cached.Body));
|
||||
|
||||
response.Content = new StreamContent(content);
|
||||
|
||||
foreach (var header in cached.ContentHeaders)
|
||||
{
|
||||
response.Content.Headers.Add(header.Key, header.Value);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@ -109,7 +117,10 @@ namespace Ocelot.Cache.Middleware
|
||||
body = Convert.ToBase64String(content);
|
||||
}
|
||||
|
||||
var cached = new CachedResponse(statusCode, headers, body);
|
||||
var contentHeaders = response?.Content?.Headers.ToDictionary(v => v.Key, v => v.Value);
|
||||
|
||||
|
||||
var cached = new CachedResponse(statusCode, headers, body, contentHeaders);
|
||||
return cached;
|
||||
}
|
||||
}
|
||||
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
namespace Ocelot.DependencyInjection
|
||||
{
|
||||
public interface IOcelotAdministrationBuilder
|
||||
{
|
||||
IOcelotAdministrationBuilder AddRafty();
|
||||
}
|
||||
}
|
@ -1,12 +1,16 @@
|
||||
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;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Ocelot.DependencyInjection
|
||||
{
|
||||
public interface IOcelotBuilder
|
||||
{
|
||||
IOcelotBuilder AddStoreOcelotConfigurationInConsul();
|
||||
IOcelotBuilder AddCacheManager(Action<ConfigurationBuilderCachePart> settings);
|
||||
IOcelotBuilder AddOpenTracing(Action<ButterflyOptions> settings);
|
||||
IOcelotAdministrationBuilder AddAdministration(string path, string secret);
|
||||
IOcelotBuilder AddDelegatingHandler(Func<DelegatingHandler> delegatingHandler);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Ocelot.Raft;
|
||||
using Rafty.Concensus;
|
||||
using Rafty.FiniteStateMachine;
|
||||
using Rafty.Infrastructure;
|
||||
using Rafty.Log;
|
||||
|
||||
namespace Ocelot.DependencyInjection
|
||||
{
|
||||
public class OcelotAdministrationBuilder : IOcelotAdministrationBuilder
|
||||
{
|
||||
private readonly IServiceCollection _services;
|
||||
private readonly IConfiguration _configurationRoot;
|
||||
|
||||
public OcelotAdministrationBuilder(IServiceCollection services, IConfiguration configurationRoot)
|
||||
{
|
||||
_configurationRoot = configurationRoot;
|
||||
_services = services;
|
||||
}
|
||||
|
||||
public IOcelotAdministrationBuilder AddRafty()
|
||||
{
|
||||
var settings = new InMemorySettings(4000, 5000, 100, 5000);
|
||||
_services.AddSingleton<ILog, SqlLiteLog>();
|
||||
_services.AddSingleton<IFiniteStateMachine, OcelotFiniteStateMachine>();
|
||||
_services.AddSingleton<ISettings>(settings);
|
||||
_services.AddSingleton<IPeersProvider, FilePeersProvider>();
|
||||
_services.AddSingleton<INode, Node>();
|
||||
_services.Configure<FilePeers>(_configurationRoot);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,307 +1,295 @@
|
||||
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 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.Reflection;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using IdentityServer4.AccessTokenValidation;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using FileConfigurationProvider = Ocelot.Configuration.Provider.FileConfigurationProvider;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using Butterfly.Client.AspNetCore;
|
||||
|
||||
namespace Ocelot.DependencyInjection
|
||||
{
|
||||
public class OcelotBuilder : IOcelotBuilder
|
||||
{
|
||||
private readonly IServiceCollection _services;
|
||||
private readonly IConfiguration _configurationRoot;
|
||||
private IDelegatingHandlerHandlerProvider _provider;
|
||||
|
||||
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>();
|
||||
_services.TryAddSingleton<IDelegatingHandlerHandlerProviderFactory, DelegatingHandlerHandlerProviderFactory>();
|
||||
_services.TryAddSingleton<IDelegatingHandlerHandlerHouse, DelegatingHandlerHandlerHouse>();
|
||||
|
||||
// see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc
|
||||
// could maybe use a scoped data repository
|
||||
_services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
_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());
|
||||
|
||||
//these get picked out later and added to http request
|
||||
_provider = new DelegatingHandlerHandlerProvider();
|
||||
_services.TryAddSingleton<IDelegatingHandlerHandlerProvider>(_provider);
|
||||
_services.AddTransient<ITracingHandler, NoTracingHandler>();
|
||||
}
|
||||
|
||||
public IOcelotAdministrationBuilder AddAdministration(string path, string secret)
|
||||
{
|
||||
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 AddDelegatingHandler(Func<DelegatingHandler> delegatingHandler)
|
||||
{
|
||||
_provider.Add(delegatingHandler);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IOcelotBuilder AddOpenTracing(Action<ButterflyOptions> settings)
|
||||
{
|
||||
_services.AddTransient<ITracingHandler, OcelotHttpTracingHandler>();
|
||||
_services.AddButterfly(settings);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IOcelotBuilder AddStoreOcelotConfigurationInConsul()
|
||||
{
|
||||
var serviceDiscoveryPort = _configurationRoot.GetValue("GlobalConfiguration:ServiceDiscoveryProvider:Port", 0);
|
||||
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 }
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Provider;
|
||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||
using Ocelot.Errors;
|
||||
using Ocelot.Responses;
|
||||
|
@ -62,7 +62,7 @@ namespace Ocelot.Errors.Middleware
|
||||
private async Task TrySetGlobalRequestId(HttpContext context)
|
||||
{
|
||||
//try and get the global request id and set it for logs...
|
||||
//shoudl this basically be immutable per request...i guess it should!
|
||||
//should this basically be immutable per request...i guess it should!
|
||||
//first thing is get config
|
||||
var configuration = await _configProvider.Get();
|
||||
|
||||
|
@ -33,6 +33,7 @@
|
||||
UnmappableRequestError,
|
||||
RateLimitOptionsError,
|
||||
PathTemplateDoesntStartWithForwardSlash,
|
||||
FileValidationFailedError
|
||||
FileValidationFailedError,
|
||||
UnableToFindDelegatingHandlerProviderError
|
||||
}
|
||||
}
|
||||
|
@ -9,4 +9,4 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Configuration.Provider;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.LoadBalancer.LoadBalancers;
|
||||
using Ocelot.Logging;
|
||||
@ -46,11 +45,14 @@ namespace Ocelot.LoadBalancer.Middleware
|
||||
}
|
||||
|
||||
var uriBuilder = new UriBuilder(DownstreamRequest.RequestUri);
|
||||
|
||||
uriBuilder.Host = hostAndPort.Data.DownstreamHost;
|
||||
|
||||
if (hostAndPort.Data.DownstreamPort > 0)
|
||||
{
|
||||
uriBuilder.Port = hostAndPort.Data.DownstreamPort;
|
||||
}
|
||||
|
||||
DownstreamRequest.RequestUri = uriBuilder.Uri;
|
||||
|
||||
try
|
||||
|
@ -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,22 @@
|
||||
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,
|
||||
string reRouteKey,
|
||||
bool isTracing)
|
||||
{
|
||||
return new OkResponse<Request>(new Request(httpRequestMessage, isQos, qosProvider, allowAutoRedirect, useCookieContainer, reRouteKey, isTracing));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,20 @@
|
||||
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,
|
||||
string reRouteKe,
|
||||
bool isTracing);
|
||||
}
|
||||
}
|
||||
|
@ -1,66 +1,67 @@
|
||||
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.ReRouteKey,
|
||||
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,35 @@
|
||||
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,
|
||||
string reRouteKey,
|
||||
bool isTracing
|
||||
)
|
||||
{
|
||||
HttpRequestMessage = httpRequestMessage;
|
||||
IsQos = isQos;
|
||||
QosProvider = qosProvider;
|
||||
AllowAutoRedirect = allowAutoRedirect;
|
||||
UseCookieContainer = useCookieContainer;
|
||||
ReRouteKey = reRouteKey;
|
||||
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; }
|
||||
public string ReRouteKey { get; private set; }
|
||||
}
|
||||
}
|
49
src/Ocelot/Requester/DelegatingHandlerHandlerHouse.cs
Normal file
49
src/Ocelot/Requester/DelegatingHandlerHandlerHouse.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.Errors;
|
||||
using Ocelot.Responses;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
public class DelegatingHandlerHandlerHouse : IDelegatingHandlerHandlerHouse
|
||||
{
|
||||
private readonly IDelegatingHandlerHandlerProviderFactory _factory;
|
||||
private readonly ConcurrentDictionary<string, IDelegatingHandlerHandlerProvider> _housed;
|
||||
|
||||
public DelegatingHandlerHandlerHouse(IDelegatingHandlerHandlerProviderFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
_housed = new ConcurrentDictionary<string, IDelegatingHandlerHandlerProvider>();
|
||||
}
|
||||
|
||||
public Response<IDelegatingHandlerHandlerProvider> Get(Request.Request request)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_housed.TryGetValue(request.ReRouteKey, out var provider))
|
||||
{
|
||||
//todo once day we might need a check here to see if we need to create a new provider
|
||||
provider = _housed[request.ReRouteKey];
|
||||
return new OkResponse<IDelegatingHandlerHandlerProvider>(provider);
|
||||
}
|
||||
|
||||
provider = _factory.Get(request);
|
||||
AddHoused(request.ReRouteKey, provider);
|
||||
return new OkResponse<IDelegatingHandlerHandlerProvider>(provider);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new ErrorResponse<IDelegatingHandlerHandlerProvider>(new List<Error>()
|
||||
{
|
||||
new UnableToFindDelegatingHandlerProviderError($"unabe to find delegating handler provider for {request.ReRouteKey} exception is {ex}")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void AddHoused(string key, IDelegatingHandlerHandlerProvider provider)
|
||||
{
|
||||
_housed.AddOrUpdate(key, provider, (k, v) => provider);
|
||||
}
|
||||
}
|
||||
}
|
28
src/Ocelot/Requester/DelegatingHandlerHandlerProvider.cs
Normal file
28
src/Ocelot/Requester/DelegatingHandlerHandlerProvider.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
public class DelegatingHandlerHandlerProvider : IDelegatingHandlerHandlerProvider
|
||||
{
|
||||
private readonly Dictionary<int, Func<DelegatingHandler>> _handlers;
|
||||
|
||||
public DelegatingHandlerHandlerProvider()
|
||||
{
|
||||
_handlers = new Dictionary<int, Func<DelegatingHandler>>();
|
||||
}
|
||||
|
||||
public void Add(Func<DelegatingHandler> handler)
|
||||
{
|
||||
var key = _handlers.Count == 0 ? 0 : _handlers.Count + 1;
|
||||
_handlers[key] = handler;
|
||||
}
|
||||
|
||||
public List<Func<DelegatingHandler>> Get()
|
||||
{
|
||||
return _handlers.Count > 0 ? _handlers.OrderBy(x => x.Key).Select(x => x.Value).ToList() : new List<Func<DelegatingHandler>>();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
using System.Net.Http;
|
||||
using Ocelot.Logging;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
public class DelegatingHandlerHandlerProviderFactory : IDelegatingHandlerHandlerProviderFactory
|
||||
{
|
||||
private readonly ITracingHandler _tracingHandler;
|
||||
private readonly IOcelotLoggerFactory _loggerFactory;
|
||||
private readonly IDelegatingHandlerHandlerProvider _allRoutesProvider;
|
||||
|
||||
public DelegatingHandlerHandlerProviderFactory(IOcelotLoggerFactory loggerFactory, IDelegatingHandlerHandlerProvider allRoutesProvider, ITracingHandler tracingHandler)
|
||||
{
|
||||
_tracingHandler = tracingHandler;
|
||||
_loggerFactory = loggerFactory;
|
||||
_allRoutesProvider = allRoutesProvider;
|
||||
}
|
||||
|
||||
public IDelegatingHandlerHandlerProvider Get(Request.Request request)
|
||||
{
|
||||
var handlersAppliedToAll = _allRoutesProvider.Get();
|
||||
|
||||
var provider = new DelegatingHandlerHandlerProvider();
|
||||
|
||||
foreach (var handler in handlersAppliedToAll)
|
||||
{
|
||||
provider.Add(handler);
|
||||
}
|
||||
|
||||
if (request.IsTracing)
|
||||
{
|
||||
provider.Add(() => (DelegatingHandler)_tracingHandler);
|
||||
}
|
||||
|
||||
if (request.IsQos)
|
||||
{
|
||||
provider.Add(() => new PollyCircuitBreakingDelegatingHandler(request.QosProvider, _loggerFactory));
|
||||
}
|
||||
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,66 +1,42 @@
|
||||
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 System.Linq;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
public class HttpClientBuilder : IHttpClientBuilder
|
||||
{
|
||||
private readonly IDelegatingHandlerHandlerHouse _house;
|
||||
|
||||
public HttpClientBuilder(IDelegatingHandlerHandlerHouse house)
|
||||
{
|
||||
_house = house;
|
||||
}
|
||||
|
||||
public IHttpClient Create(Request.Request request)
|
||||
{
|
||||
var httpclientHandler = new HttpClientHandler { AllowAutoRedirect = request.AllowAutoRedirect, UseCookies = request.UseCookieContainer};
|
||||
|
||||
var client = new HttpClient(CreateHttpMessageHandler(httpclientHandler, request));
|
||||
|
||||
return new HttpClientWrapper(client);
|
||||
}
|
||||
|
||||
private HttpMessageHandler CreateHttpMessageHandler(HttpMessageHandler httpMessageHandler, Request.Request request)
|
||||
{
|
||||
var provider = _house.Get(request);
|
||||
|
||||
//todo handle error
|
||||
provider.Data.Get()
|
||||
.Select(handler => handler)
|
||||
.Reverse()
|
||||
.ToList()
|
||||
.ForEach(handler =>
|
||||
{
|
||||
var delegatingHandler = handler();
|
||||
delegatingHandler.InnerHandler = httpMessageHandler;
|
||||
httpMessageHandler = delegatingHandler;
|
||||
});
|
||||
return httpMessageHandler;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
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;
|
||||
@ -14,21 +12,24 @@ namespace Ocelot.Requester
|
||||
{
|
||||
private readonly IHttpClientCache _cacheHandlers;
|
||||
private readonly IOcelotLogger _logger;
|
||||
private readonly IDelegatingHandlerHandlerHouse _house;
|
||||
|
||||
public HttpClientHttpRequester(IOcelotLoggerFactory loggerFactory,
|
||||
IHttpClientCache cacheHandlers)
|
||||
IHttpClientCache cacheHandlers,
|
||||
IDelegatingHandlerHandlerHouse house)
|
||||
{
|
||||
_logger = loggerFactory.CreateLogger<HttpClientHttpRequester>();
|
||||
_cacheHandlers = cacheHandlers;
|
||||
_house = house;
|
||||
}
|
||||
|
||||
public async Task<Response<HttpResponseMessage>> GetResponse(Request.Request request)
|
||||
{
|
||||
var builder = new HttpClientBuilder();
|
||||
var builder = new HttpClientBuilder(_house);
|
||||
|
||||
var cacheKey = GetCacheKey(request, builder);
|
||||
var cacheKey = GetCacheKey(request);
|
||||
|
||||
var httpClient = GetHttpClient(cacheKey, builder, request.UseCookieContainer, request.AllowAutoRedirect);
|
||||
var httpClient = GetHttpClient(cacheKey, builder, request);
|
||||
|
||||
try
|
||||
{
|
||||
@ -56,28 +57,28 @@ namespace Ocelot.Requester
|
||||
|
||||
}
|
||||
|
||||
private IHttpClient GetHttpClient(string cacheKey, IHttpClientBuilder builder, bool useCookieContainer, bool allowAutoRedirect)
|
||||
private IHttpClient GetHttpClient(string cacheKey, IHttpClientBuilder builder, Request.Request request)
|
||||
{
|
||||
var httpClient = _cacheHandlers.Get(cacheKey);
|
||||
|
||||
if (httpClient == null)
|
||||
{
|
||||
httpClient = builder.Create(useCookieContainer, allowAutoRedirect);
|
||||
httpClient = builder.Create(request);
|
||||
}
|
||||
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
private string GetCacheKey(Request.Request request, IHttpClientBuilder builder)
|
||||
private string GetCacheKey(Request.Request request)
|
||||
{
|
||||
string baseUrl = $"{request.HttpRequestMessage.RequestUri.Scheme}://{request.HttpRequestMessage.RequestUri.Authority}";
|
||||
var 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
23
src/Ocelot/Requester/HttpClientWrapper.cs
Normal file
23
src/Ocelot/Requester/HttpClientWrapper.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
}
|
9
src/Ocelot/Requester/IDelegatingHandlerHandlerHouse.cs
Normal file
9
src/Ocelot/Requester/IDelegatingHandlerHandlerHouse.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using Ocelot.Responses;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
public interface IDelegatingHandlerHandlerHouse
|
||||
{
|
||||
Response<IDelegatingHandlerHandlerProvider> Get(Request.Request request);
|
||||
}
|
||||
}
|
12
src/Ocelot/Requester/IDelegatingHandlerHandlerProvider.cs
Normal file
12
src/Ocelot/Requester/IDelegatingHandlerHandlerProvider.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
public interface IDelegatingHandlerHandlerProvider
|
||||
{
|
||||
void Add(Func<DelegatingHandler> handler);
|
||||
List<Func<DelegatingHandler>> Get();
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
public interface IDelegatingHandlerHandlerProviderFactory
|
||||
{
|
||||
IDelegatingHandlerHandlerProvider Get(Request.Request request);
|
||||
}
|
||||
}
|
@ -1,27 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Requester.QoS;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using Ocelot.Configuration;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
public interface IHttpClientBuilder
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Sets a PollyCircuitBreakingDelegatingHandler .
|
||||
/// </summary>
|
||||
IHttpClientBuilder WithQos(IQoSProvider qosProvider, IOcelotLogger logger);
|
||||
|
||||
/// <summary>
|
||||
/// Creates the <see cref="HttpClient"/>
|
||||
/// </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);
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
IHttpClient Create(Request.Request request);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
71
src/Ocelot/Requester/OcelotHttpTracingHandler.cs
Normal file
71
src/Ocelot/Requester/OcelotHttpTracingHandler.cs
Normal file
@ -0,0 +1,71 @@
|
||||
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 interface ITracingHandler
|
||||
{
|
||||
}
|
||||
|
||||
public class NoTracingHandler : DelegatingHandler, ITracingHandler
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class OcelotHttpTracingHandler : DelegatingHandler, ITracingHandler
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -16,10 +16,10 @@ namespace Ocelot.Requester
|
||||
|
||||
public PollyCircuitBreakingDelegatingHandler(
|
||||
IQoSProvider qoSProvider,
|
||||
IOcelotLogger logger)
|
||||
IOcelotLoggerFactory loggerFactory)
|
||||
{
|
||||
_qoSProvider = qoSProvider;
|
||||
_logger = logger;
|
||||
_logger = loggerFactory.CreateLogger<PollyCircuitBreakingDelegatingHandler>();
|
||||
}
|
||||
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
|
@ -0,0 +1,12 @@
|
||||
using Ocelot.Errors;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
public class UnableToFindDelegatingHandlerProviderError : Error
|
||||
{
|
||||
public UnableToFindDelegatingHandlerProviderError(string message)
|
||||
: base(message, OcelotErrorCode.UnableToFindDelegatingHandlerProviderError)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -55,7 +55,7 @@ namespace Ocelot.Responder
|
||||
|
||||
using (Stream stream = new MemoryStream(content))
|
||||
{
|
||||
if (response.StatusCode != HttpStatusCode.NotModified)
|
||||
if (response.StatusCode != HttpStatusCode.NotModified && context.Response.ContentLength != 0)
|
||||
{
|
||||
await stream.CopyToAsync(context.Response.Body);
|
||||
}
|
||||
@ -79,4 +79,4 @@ namespace Ocelot.Responder
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Consul;
|
||||
using Ocelot.Infrastructure.Extensions;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.ServiceDiscovery
|
||||
@ -11,13 +12,18 @@ namespace Ocelot.ServiceDiscovery
|
||||
public class ConsulServiceDiscoveryProvider : IServiceDiscoveryProvider
|
||||
{
|
||||
private readonly ConsulRegistryConfiguration _consulConfig;
|
||||
private readonly IOcelotLogger _logger;
|
||||
private readonly ConsulClient _consul;
|
||||
private const string VersionPrefix = "version-";
|
||||
|
||||
public ConsulServiceDiscoveryProvider(ConsulRegistryConfiguration consulRegistryConfiguration)
|
||||
{
|
||||
public ConsulServiceDiscoveryProvider(ConsulRegistryConfiguration consulRegistryConfiguration, IOcelotLoggerFactory factory)
|
||||
{;
|
||||
_logger = factory.CreateLogger<ConsulServiceDiscoveryProvider>();
|
||||
|
||||
var consulHost = string.IsNullOrEmpty(consulRegistryConfiguration?.HostName) ? "localhost" : consulRegistryConfiguration.HostName;
|
||||
|
||||
var consulPort = consulRegistryConfiguration?.Port ?? 8500;
|
||||
|
||||
_consulConfig = new ConsulRegistryConfiguration(consulHost, consulPort, consulRegistryConfiguration?.KeyOfServiceInConsul);
|
||||
|
||||
_consul = new ConsulClient(config =>
|
||||
@ -30,7 +36,19 @@ namespace Ocelot.ServiceDiscovery
|
||||
{
|
||||
var queryResult = await _consul.Health.Service(_consulConfig.KeyOfServiceInConsul, string.Empty, true);
|
||||
|
||||
var services = queryResult.Response.Select(BuildService);
|
||||
var services = new List<Service>();
|
||||
|
||||
foreach (var serviceEntry in queryResult.Response)
|
||||
{
|
||||
if (IsValid(serviceEntry))
|
||||
{
|
||||
services.Add(BuildService(serviceEntry));
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogError($"Unable to use service Address: {serviceEntry.Service.Address} and Port: {serviceEntry.Service.Port} as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0");
|
||||
}
|
||||
}
|
||||
|
||||
return services.ToList();
|
||||
}
|
||||
@ -45,6 +63,16 @@ namespace Ocelot.ServiceDiscovery
|
||||
serviceEntry.Service.Tags ?? Enumerable.Empty<string>());
|
||||
}
|
||||
|
||||
private bool IsValid(ServiceEntry serviceEntry)
|
||||
{
|
||||
if (serviceEntry.Service.Address.Contains("http://") || serviceEntry.Service.Address.Contains("https://") || serviceEntry.Service.Port <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private string GetVersionFromStrings(IEnumerable<string> strings)
|
||||
{
|
||||
return strings
|
||||
|
@ -1,11 +1,19 @@
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.ServiceDiscovery
|
||||
{
|
||||
public class ServiceDiscoveryProviderFactory : IServiceDiscoveryProviderFactory
|
||||
{
|
||||
private readonly IOcelotLoggerFactory _factory;
|
||||
|
||||
public ServiceDiscoveryProviderFactory(IOcelotLoggerFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
public IServiceDiscoveryProvider Get(ServiceProviderConfiguration serviceConfig, ReRoute reRoute)
|
||||
{
|
||||
if (reRoute.UseServiceDiscovery)
|
||||
@ -28,7 +36,7 @@ namespace Ocelot.ServiceDiscovery
|
||||
private IServiceDiscoveryProvider GetServiceDiscoveryProvider(string keyOfServiceInConsul, string providerHostName, int providerPort)
|
||||
{
|
||||
var consulRegistryConfiguration = new ConsulRegistryConfiguration(providerHostName, providerPort, keyOfServiceInConsul);
|
||||
return new ConsulServiceDiscoveryProvider(consulRegistryConfiguration);
|
||||
return new ConsulServiceDiscoveryProvider(consulRegistryConfiguration, _factory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,6 +61,7 @@ namespace Ocelot.AcceptanceTests
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||
.And(x => _steps.ThenTheContentLengthIs(16))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
|
116
test/Ocelot.AcceptanceTests/HttpDelegatingHandlersTests.cs
Normal file
116
test/Ocelot.AcceptanceTests/HttpDelegatingHandlersTests.cs
Normal file
@ -0,0 +1,116 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Configuration.File;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
public class HttpDelegatingHandlersTests
|
||||
{
|
||||
private IWebHost _builder;
|
||||
private readonly Steps _steps;
|
||||
private string _downstreamPath;
|
||||
|
||||
public HttpDelegatingHandlersTests()
|
||||
{
|
||||
_steps = new Steps();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_call_handlers()
|
||||
{
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/",
|
||||
DownstreamScheme = "http",
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||
{
|
||||
new FileHostAndPort
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 61879,
|
||||
}
|
||||
},
|
||||
UpstreamPathTemplate = "/",
|
||||
UpstreamHttpMethod = new List<string> { "Get" },
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var handlerOne = new FakeHandler();
|
||||
var handlerTwo = new FakeHandler();
|
||||
|
||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:61879", "/", 200, "Hello from Laura"))
|
||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||
.And(x => _steps.GivenOcelotIsRunningWithHandlers(handlerOne, handlerTwo))
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||
.And(x => ThenTheHandlersAreCalledCorrectly(handlerOne, handlerTwo))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenTheHandlersAreCalledCorrectly(FakeHandler one, FakeHandler two)
|
||||
{
|
||||
one.TimeCalled.ShouldBeLessThan(two.TimeCalled);
|
||||
}
|
||||
|
||||
class FakeHandler : DelegatingHandler
|
||||
{
|
||||
|
||||
public DateTime TimeCalled { get; private set; }
|
||||
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
TimeCalled = DateTime.Now;
|
||||
return await base.SendAsync(request, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
|
||||
{
|
||||
_builder = new WebHostBuilder()
|
||||
.UseUrls(baseUrl)
|
||||
.UseKestrel()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseIISIntegration()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UsePathBase(basePath);
|
||||
app.Run(async context =>
|
||||
{
|
||||
_downstreamPath = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
|
||||
|
||||
if (_downstreamPath != basePath)
|
||||
{
|
||||
context.Response.StatusCode = statusCode;
|
||||
await context.Response.WriteAsync("downstream path didnt match base path");
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Response.StatusCode = statusCode;
|
||||
await context.Response.WriteAsync(responseBody);
|
||||
}
|
||||
});
|
||||
})
|
||||
.Build();
|
||||
|
||||
_builder.Start();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -88,6 +88,38 @@ namespace Ocelot.AcceptanceTests
|
||||
_ocelotClient = _ocelotServer.CreateClient();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is annoying cos it should be in the constructor but we need to set up the file before calling startup so its a step.
|
||||
/// </summary>
|
||||
public void GivenOcelotIsRunningWithHandlers(DelegatingHandler handlerOne, DelegatingHandler handlerTwo)
|
||||
{
|
||||
_webHostBuilder = new WebHostBuilder();
|
||||
|
||||
_webHostBuilder.ConfigureServices(s =>
|
||||
{
|
||||
s.AddSingleton(_webHostBuilder);
|
||||
s.AddOcelot()
|
||||
.AddDelegatingHandler(() => handlerOne)
|
||||
.AddDelegatingHandler(() => handlerTwo);
|
||||
});
|
||||
_webHostBuilder.ConfigureAppConfiguration((hostingContext, config) =>
|
||||
{
|
||||
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||
config.AddJsonFile("configuration.json");
|
||||
config.AddEnvironmentVariables();
|
||||
}).Configure(a =>
|
||||
{
|
||||
a.UseOcelot().Wait();
|
||||
});
|
||||
|
||||
_ocelotServer = new TestServer(_webHostBuilder);
|
||||
|
||||
_ocelotClient = _ocelotServer.CreateClient();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is annoying cos it should be in the constructor but we need to set up the file before calling startup so its a step.
|
||||
/// </summary>
|
||||
@ -425,5 +457,10 @@ namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
_response.Headers.GetValues(RequestIdKey).First().ShouldBe(expected);
|
||||
}
|
||||
|
||||
public void ThenTheContentLengthIs(int expected)
|
||||
{
|
||||
_response.Content.Headers.ContentLength.ShouldBe(expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,37 @@
|
||||
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 Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Ocelot.DependencyInjection;
|
||||
using Ocelot.Middleware;
|
||||
|
||||
namespace Ocelot.ManualTest
|
||||
{
|
||||
public class ManualTestStartup
|
||||
{
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddAuthentication()
|
||||
.AddJwtBearer("TestKey", x =>
|
||||
{
|
||||
x.Authority = "test";
|
||||
x.Audience = "test";
|
||||
});
|
||||
|
||||
services.AddOcelot()
|
||||
.AddCacheManager(x =>
|
||||
{
|
||||
x.WithDictionaryHandle();
|
||||
})
|
||||
.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,318 @@
|
||||
{
|
||||
"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": 5001
|
||||
}
|
||||
],
|
||||
"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" ],
|
||||
"HttpHandlerOptions": {
|
||||
"AllowAutoRedirect": true,
|
||||
"UseCookieContainer": true
|
||||
},
|
||||
"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",
|
||||
"HttpHandlerOptions": {
|
||||
"AllowAutoRedirect": true,
|
||||
"UseCookieContainer": true,
|
||||
"UseTracing": true
|
||||
},
|
||||
"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" ],
|
||||
"HttpHandlerOptions": {
|
||||
"AllowAutoRedirect": true,
|
||||
"UseCookieContainer": true,
|
||||
"UseTracing": true
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,131 @@
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
|
||||
namespace Ocelot.UnitTests.Cache
|
||||
{
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using CacheManager.Core;
|
||||
using Shouldly;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Moq;
|
||||
using Ocelot.Cache;
|
||||
using Ocelot.Cache.Middleware;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.DownstreamRouteFinder;
|
||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Responses;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
public class OutputCacheMiddlewareRealCacheTests : ServerHostedMiddlewareTest
|
||||
{
|
||||
private IOcelotCache<CachedResponse> _cacheManager;
|
||||
private CachedResponse _response;
|
||||
private IRequestScopedDataRepository _repo;
|
||||
|
||||
public OutputCacheMiddlewareRealCacheTests()
|
||||
{
|
||||
ScopedRepository
|
||||
.Setup(sr => sr.Get<HttpRequestMessage>("DownstreamRequest"))
|
||||
.Returns(new OkResponse<HttpRequestMessage>(new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123")));
|
||||
|
||||
GivenTheTestServerIsConfigured();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_cache_content_headers()
|
||||
{
|
||||
var content = new StringContent("{\"Test\": 1}")
|
||||
{
|
||||
Headers = { ContentType = new MediaTypeHeaderValue("application/json")}
|
||||
};
|
||||
|
||||
var response = new HttpResponseMessage(HttpStatusCode.OK)
|
||||
{
|
||||
Content = content,
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenResponseIsNotCached(response))
|
||||
.And(x => x.GivenTheDownstreamRouteIs())
|
||||
.And(x => x.GivenThereAreNoErrors())
|
||||
.And(x => x.GivenThereIsADownstreamUrl())
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
.Then(x => x.ThenTheContentTypeHeaderIsCached())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenTheContentTypeHeaderIsCached()
|
||||
{
|
||||
var result = _cacheManager.Get("GET-https://some.url/blah?abcd=123", "kanken");
|
||||
var header = result.ContentHeaders["Content-Type"];
|
||||
header.First().ShouldBe("application/json");
|
||||
}
|
||||
|
||||
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
|
||||
{
|
||||
var cacheManagerOutputCache = CacheFactory.Build<CachedResponse>("OcelotOutputCache", x =>
|
||||
{
|
||||
x.WithDictionaryHandle();
|
||||
});
|
||||
|
||||
_cacheManager = new OcelotCacheManagerCache<CachedResponse>(cacheManagerOutputCache);
|
||||
|
||||
services.AddSingleton<ICacheManager<CachedResponse>>(cacheManagerOutputCache);
|
||||
services.AddSingleton<IOcelotCache<CachedResponse>>(_cacheManager);
|
||||
|
||||
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
|
||||
|
||||
services.AddLogging();
|
||||
services.AddSingleton(_cacheManager);
|
||||
services.AddSingleton(ScopedRepository.Object);
|
||||
services.AddSingleton<IRegionCreator, RegionCreator>();
|
||||
}
|
||||
|
||||
protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app)
|
||||
{
|
||||
app.UseOutputCacheMiddleware();
|
||||
}
|
||||
|
||||
private void GivenResponseIsNotCached(HttpResponseMessage message)
|
||||
{
|
||||
ScopedRepository
|
||||
.Setup(x => x.Get<HttpResponseMessage>("HttpResponseMessage"))
|
||||
.Returns(new OkResponse<HttpResponseMessage>(message));
|
||||
}
|
||||
|
||||
private void GivenTheDownstreamRouteIs()
|
||||
{
|
||||
var reRoute = new ReRouteBuilder()
|
||||
.WithIsCached(true)
|
||||
.WithCacheOptions(new CacheOptions(100, "kanken"))
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.Build();
|
||||
|
||||
var downstreamRoute = new DownstreamRoute(new List<PlaceholderNameAndValue>(), reRoute);
|
||||
|
||||
ScopedRepository
|
||||
.Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>()))
|
||||
.Returns(new OkResponse<DownstreamRoute>(downstreamRoute));
|
||||
}
|
||||
|
||||
private void GivenThereAreNoErrors()
|
||||
{
|
||||
ScopedRepository
|
||||
.Setup(x => x.Get<bool>("OcelotMiddlewareError"))
|
||||
.Returns(new OkResponse<bool>(false));
|
||||
}
|
||||
|
||||
private void GivenThereIsADownstreamUrl()
|
||||
{
|
||||
ScopedRepository
|
||||
.Setup(x => x.Get<string>("DownstreamUrl"))
|
||||
.Returns(new OkResponse<string>("anything"));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
namespace Ocelot.UnitTests.Cache
|
||||
using System.Net;
|
||||
|
||||
namespace Ocelot.UnitTests.Cache
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@ -36,7 +38,7 @@
|
||||
[Fact]
|
||||
public void should_returned_cached_item_when_it_is_in_cache()
|
||||
{
|
||||
var cachedResponse = new CachedResponse();
|
||||
var cachedResponse = new CachedResponse(HttpStatusCode.OK, new Dictionary<string, IEnumerable<string>>(), "", new Dictionary<string, IEnumerable<string>>());
|
||||
this.Given(x => x.GivenThereIsACachedResponse(cachedResponse))
|
||||
.And(x => x.GivenTheDownstreamRouteIs())
|
||||
.And(x => x.GivenThereIsADownstreamUrl())
|
||||
|
@ -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,8 +1,7 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using CacheManager.Core;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Hosting.Internal;
|
||||
@ -13,8 +12,10 @@ using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.Configuration.Setter;
|
||||
using Ocelot.DependencyInjection;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Requester;
|
||||
using Ocelot.UnitTests.Requester;
|
||||
using Shouldly;
|
||||
using System;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
@ -22,11 +23,11 @@ namespace Ocelot.UnitTests.DependencyInjection
|
||||
{
|
||||
public class OcelotBuilderTests
|
||||
{
|
||||
private IServiceCollection _services;
|
||||
private readonly IServiceCollection _services;
|
||||
private IServiceProvider _serviceProvider;
|
||||
private IConfiguration _configRoot;
|
||||
private readonly IConfiguration _configRoot;
|
||||
private IOcelotBuilder _ocelotBuilder;
|
||||
private int _maxRetries;
|
||||
private readonly int _maxRetries;
|
||||
|
||||
public OcelotBuilderTests()
|
||||
{
|
||||
@ -40,6 +41,19 @@ namespace Ocelot.UnitTests.DependencyInjection
|
||||
}
|
||||
private Exception _ex;
|
||||
|
||||
[Fact]
|
||||
public void should_add_delegating_handlers()
|
||||
{
|
||||
var fakeOne = new FakeDelegatingHandler(0);
|
||||
var fakeTwo = new FakeDelegatingHandler(1);
|
||||
|
||||
this.Given(x => WhenISetUpOcelotServices())
|
||||
.When(x => AddDelegate(fakeOne))
|
||||
.And(x => AddDelegate(fakeTwo))
|
||||
.Then(x => ThenTheProviderIsRegisteredAndReturnsHandlers())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_set_up_services()
|
||||
{
|
||||
@ -56,7 +70,7 @@ namespace Ocelot.UnitTests.DependencyInjection
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Fact]
|
||||
public void should_set_up_cache_manager()
|
||||
{
|
||||
@ -76,7 +90,7 @@ namespace Ocelot.UnitTests.DependencyInjection
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Fact]
|
||||
public void should_set_up_rafty()
|
||||
{
|
||||
this.Given(x => WhenISetUpOcelotServices())
|
||||
@ -96,6 +110,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()
|
||||
{
|
||||
@ -111,6 +135,17 @@ namespace Ocelot.UnitTests.DependencyInjection
|
||||
path.Path.ShouldBe("/administration");
|
||||
}
|
||||
|
||||
private void ThenTheProviderIsRegisteredAndReturnsHandlers()
|
||||
{
|
||||
_serviceProvider = _services.BuildServiceProvider();
|
||||
var provider = _serviceProvider.GetService<IDelegatingHandlerHandlerProvider>();
|
||||
var handlers = provider.Get();
|
||||
var handler = (FakeDelegatingHandler)handlers[0].Invoke();
|
||||
handler.Order.ShouldBe(0);
|
||||
handler = (FakeDelegatingHandler)handlers[1].Invoke();
|
||||
handler.Order.ShouldBe(1);
|
||||
}
|
||||
|
||||
private void OnlyOneVersionOfEachCacheIsRegistered()
|
||||
{
|
||||
var outputCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache<CachedResponse>));
|
||||
@ -149,6 +184,11 @@ namespace Ocelot.UnitTests.DependencyInjection
|
||||
}
|
||||
}
|
||||
|
||||
private void AddDelegate(DelegatingHandler handler)
|
||||
{
|
||||
_ocelotBuilder.AddDelegatingHandler(() => handler);
|
||||
}
|
||||
|
||||
private void ThenAnOcelotBuilderIsReturned()
|
||||
{
|
||||
_ocelotBuilder.ShouldBeOfType<OcelotBuilder>();
|
||||
@ -193,6 +233,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 +263,18 @@ namespace Ocelot.UnitTests.DependencyInjection
|
||||
}
|
||||
}
|
||||
|
||||
private void WhenIAccessOcelotHttpTracingHandler()
|
||||
{
|
||||
try
|
||||
{
|
||||
var tracingHandler = _serviceProvider.GetService<OcelotHttpTracingHandler>();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_ex = e;
|
||||
}
|
||||
}
|
||||
|
||||
private void WhenIValidateScopes()
|
||||
{
|
||||
try
|
||||
|
@ -19,10 +19,6 @@
|
||||
<DebugSymbols>True</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Responder\HttpContextResponderTests.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Ocelot\Ocelot.csproj" />
|
||||
</ItemGroup>
|
||||
|
@ -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,6 +131,8 @@
|
||||
It.IsAny<bool>(),
|
||||
It.IsAny<IQoSProvider>(),
|
||||
It.IsAny<bool>(),
|
||||
It.IsAny<bool>(),
|
||||
It.IsAny<string>(),
|
||||
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 Response<Ocelot.Request.Request> _response;
|
||||
private string _reRouteKey;
|
||||
private readonly bool _useTracing;
|
||||
|
||||
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.ThenTheRequestContainsUseTracing())
|
||||
.Then(x => x.ThenTheRequestContainsAllowAutoRedirect())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void WhenIBuildARequest()
|
||||
{
|
||||
_response = _requestCreator.Build(_requestMessage,
|
||||
_isQos, _qoSProvider, _useCookieContainer, _allowAutoRedirect, _reRouteKey, _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,108 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using Moq;
|
||||
using Ocelot.Requester;
|
||||
using Ocelot.Responses;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.Requester
|
||||
{
|
||||
public class DelegatingHandlerHandlerHouseTests
|
||||
{
|
||||
private readonly DelegatingHandlerHandlerHouse _house;
|
||||
private Mock<IDelegatingHandlerHandlerProviderFactory> _factory;
|
||||
private readonly Mock<IDelegatingHandlerHandlerProvider> _provider;
|
||||
private Ocelot.Request.Request _request;
|
||||
private Response<IDelegatingHandlerHandlerProvider> _result;
|
||||
|
||||
public DelegatingHandlerHandlerHouseTests()
|
||||
{
|
||||
_provider = new Mock<IDelegatingHandlerHandlerProvider>();
|
||||
_factory = new Mock<IDelegatingHandlerHandlerProviderFactory>();
|
||||
_house = new DelegatingHandlerHandlerHouse(_factory.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_create_and_store_provider()
|
||||
{
|
||||
var request = new Ocelot.Request.Request(new HttpRequestMessage(), true, null, true, true, "key", false);
|
||||
|
||||
this.Given(x => GivenTheRequest(request))
|
||||
.And(x => GivenTheProviderReturns())
|
||||
.When(x => WhenIGet())
|
||||
.Then(x => ThenTheFactoryIsCalled(1))
|
||||
.And(x => ThenTheProviderIsNotNull())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_get_provider()
|
||||
{
|
||||
var request = new Ocelot.Request.Request(new HttpRequestMessage(), true, null, true, true, "key", false);
|
||||
|
||||
this.Given(x => GivenTheRequest(request))
|
||||
.And(x => GivenTheProviderReturns())
|
||||
.And(x => WhenIGet())
|
||||
.And(x => GivenTheFactoryIsCleared())
|
||||
.When(x => WhenIGet())
|
||||
.Then(x => ThenTheFactoryIsCalled(0))
|
||||
.And(x => ThenTheProviderIsNotNull())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_error()
|
||||
{
|
||||
var request = new Ocelot.Request.Request(new HttpRequestMessage(), true, null, true, true, "key", false);
|
||||
|
||||
this.Given(x => GivenTheRequest(request))
|
||||
.And(x => GivenTheProviderThrows())
|
||||
.When(x => WhenIGet())
|
||||
.And(x => ThenAnErrorIsReturned())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenAnErrorIsReturned()
|
||||
{
|
||||
_result.IsError.ShouldBeTrue();
|
||||
_result.Errors[0].ShouldBeOfType<UnableToFindDelegatingHandlerProviderError>();
|
||||
}
|
||||
|
||||
private void GivenTheProviderThrows()
|
||||
{
|
||||
_factory.Setup(x => x.Get(It.IsAny<Ocelot.Request.Request>())).Throws<Exception>();
|
||||
}
|
||||
|
||||
private void GivenTheFactoryIsCleared()
|
||||
{
|
||||
_factory = new Mock<IDelegatingHandlerHandlerProviderFactory>();
|
||||
}
|
||||
|
||||
private void ThenTheProviderIsNotNull()
|
||||
{
|
||||
_result.Data.ShouldBe(_provider.Object);
|
||||
}
|
||||
|
||||
private void WhenIGet()
|
||||
{
|
||||
_result = _house.Get(_request);
|
||||
}
|
||||
|
||||
private void GivenTheRequest(Ocelot.Request.Request request)
|
||||
{
|
||||
_request = request;
|
||||
}
|
||||
|
||||
private void GivenTheProviderReturns()
|
||||
{
|
||||
_factory.Setup(x => x.Get(It.IsAny<Ocelot.Request.Request>())).Returns(_provider.Object);
|
||||
}
|
||||
|
||||
private void ThenTheFactoryIsCalled(int times)
|
||||
{
|
||||
_factory.Verify(x => x.Get(_request), Times.Exactly(times));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using Moq;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Requester;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.Requester
|
||||
{
|
||||
public class DelegatingHandlerHandlerProviderFactoryTests
|
||||
{
|
||||
private readonly DelegatingHandlerHandlerProviderFactory _factory;
|
||||
private Mock<IOcelotLoggerFactory> _loggerFactory;
|
||||
private Ocelot.Request.Request _request;
|
||||
private IDelegatingHandlerHandlerProvider _provider;
|
||||
private readonly Mock<IDelegatingHandlerHandlerProvider> _allRoutesProvider;
|
||||
|
||||
public DelegatingHandlerHandlerProviderFactoryTests()
|
||||
{
|
||||
_allRoutesProvider = new Mock<IDelegatingHandlerHandlerProvider>();
|
||||
_loggerFactory = new Mock<IOcelotLoggerFactory>();
|
||||
_factory = new DelegatingHandlerHandlerProviderFactory(_loggerFactory.Object, _allRoutesProvider.Object, null);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_all_from_all_routes_provider_and_qos()
|
||||
{
|
||||
var handlers = new List<Func<DelegatingHandler>>
|
||||
{
|
||||
() => new FakeDelegatingHandler(0),
|
||||
() => new FakeDelegatingHandler(1)
|
||||
};
|
||||
|
||||
var request = new Ocelot.Request.Request(new HttpRequestMessage(), true, null, true, true, "", false);
|
||||
|
||||
this.Given(x => GivenTheFollowingRequest(request))
|
||||
.And(x => GivenTheAllRoutesProviderReturns(handlers))
|
||||
.When(x => WhenIGet())
|
||||
.Then(x => ThenThereIsDelegatesInProvider(3))
|
||||
.And(x => ThenTheDelegatesAreAddedCorrectly())
|
||||
.And(x => ThenItIsPolly(2))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_provider_with_no_delegates()
|
||||
{
|
||||
var request = new Ocelot.Request.Request(new HttpRequestMessage(), false, null, true, true, "", false);
|
||||
|
||||
this.Given(x => GivenTheFollowingRequest(request))
|
||||
.And(x => GivenTheAllRoutesProviderReturns())
|
||||
.When(x => WhenIGet())
|
||||
.Then(x => ThenNoDelegatesAreInTheProvider())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_provider_with_qos_delegate()
|
||||
{
|
||||
var request = new Ocelot.Request.Request(new HttpRequestMessage(), true, null, true, true, "", false);
|
||||
|
||||
this.Given(x => GivenTheFollowingRequest(request))
|
||||
.And(x => GivenTheAllRoutesProviderReturns())
|
||||
.When(x => WhenIGet())
|
||||
.Then(x => ThenThereIsDelegatesInProvider(1))
|
||||
.And(x => ThenItIsPolly(0))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenTheDelegatesAreAddedCorrectly()
|
||||
{
|
||||
var delegates = _provider.Get();
|
||||
var del = delegates[0].Invoke();
|
||||
var handler = (FakeDelegatingHandler) del;
|
||||
handler.Order.ShouldBe(0);
|
||||
|
||||
del = delegates[1].Invoke();
|
||||
handler = (FakeDelegatingHandler)del;
|
||||
handler.Order.ShouldBe(1);
|
||||
}
|
||||
|
||||
private void GivenTheAllRoutesProviderReturns()
|
||||
{
|
||||
_allRoutesProvider.Setup(x => x.Get()).Returns(new List<Func<DelegatingHandler>>());
|
||||
}
|
||||
|
||||
private void GivenTheAllRoutesProviderReturns(List<Func<DelegatingHandler>> handlers)
|
||||
{
|
||||
_allRoutesProvider.Setup(x => x.Get()).Returns(handlers);
|
||||
}
|
||||
|
||||
private void ThenItIsPolly(int i)
|
||||
{
|
||||
var delegates = _provider.Get();
|
||||
var del = delegates[i].Invoke();
|
||||
del.ShouldBeOfType<PollyCircuitBreakingDelegatingHandler>();
|
||||
}
|
||||
|
||||
private void ThenThereIsDelegatesInProvider(int count)
|
||||
{
|
||||
_provider.ShouldNotBeNull();
|
||||
_provider.Get().Count.ShouldBe(count);
|
||||
}
|
||||
|
||||
private void GivenTheFollowingRequest(Ocelot.Request.Request request)
|
||||
{
|
||||
_request = request;
|
||||
}
|
||||
|
||||
private void WhenIGet()
|
||||
{
|
||||
_provider = _factory.Get(_request);
|
||||
}
|
||||
|
||||
private void ThenNoDelegatesAreInTheProvider()
|
||||
{
|
||||
_provider.ShouldNotBeNull();
|
||||
_provider.Get().Count.ShouldBe(0);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using Ocelot.Requester;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.Requester
|
||||
{
|
||||
public class DelegatingHandlerHandlerProviderTests
|
||||
{
|
||||
private readonly DelegatingHandlerHandlerProvider _provider;
|
||||
private List<Func<DelegatingHandler>> _handlers;
|
||||
|
||||
public DelegatingHandlerHandlerProviderTests()
|
||||
{
|
||||
_provider = new DelegatingHandlerHandlerProvider();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_empty_list()
|
||||
{
|
||||
this.When(x => WhenIGet())
|
||||
.Then(x => ThenAnEmptyListIsReturned())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_get_delegating_handlers_in_order_first_in_first_out()
|
||||
{
|
||||
this.Given(x => GivenTheHandlers())
|
||||
.When(x => WhenIGet())
|
||||
.Then(x => ThenTheHandlersAreReturnedInOrder())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenAnEmptyListIsReturned()
|
||||
{
|
||||
_handlers.Count.ShouldBe(0);
|
||||
}
|
||||
|
||||
private void ThenTheHandlersAreReturnedInOrder()
|
||||
{
|
||||
var handler = (FakeDelegatingHandler)_handlers[0].Invoke();
|
||||
handler.Order.ShouldBe(0);
|
||||
handler = (FakeDelegatingHandler)_handlers[1].Invoke();
|
||||
handler.Order.ShouldBe(1);
|
||||
}
|
||||
|
||||
private void WhenIGet()
|
||||
{
|
||||
_handlers = _provider.Get();
|
||||
}
|
||||
|
||||
private void GivenTheHandlers()
|
||||
{
|
||||
_provider.Add(() => new FakeDelegatingHandler(0));
|
||||
_provider.Add(() => new FakeDelegatingHandler(1));
|
||||
}
|
||||
}
|
||||
}
|
28
test/Ocelot.UnitTests/Requester/FakeDelegatingHandler.cs
Normal file
28
test/Ocelot.UnitTests/Requester/FakeDelegatingHandler.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.UnitTests.Requester
|
||||
{
|
||||
public class FakeDelegatingHandler : DelegatingHandler
|
||||
{
|
||||
public FakeDelegatingHandler()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public FakeDelegatingHandler(int order)
|
||||
{
|
||||
Order = order;
|
||||
}
|
||||
public int Order {get;private set;}
|
||||
public DateTime TimeCalled {get;private set;}
|
||||
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
TimeCalled = DateTime.Now;
|
||||
return new HttpResponseMessage();
|
||||
}
|
||||
}
|
||||
}
|
114
test/Ocelot.UnitTests/Requester/HttpClientBuilderTests.cs
Normal file
114
test/Ocelot.UnitTests/Requester/HttpClientBuilderTests.cs
Normal file
@ -0,0 +1,114 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using Moq;
|
||||
using Ocelot.Requester;
|
||||
using Ocelot.Responses;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.Requester
|
||||
{
|
||||
public class HttpClientBuilderTests
|
||||
{
|
||||
private readonly HttpClientBuilder _builder;
|
||||
private readonly Mock<IDelegatingHandlerHandlerHouse> _house;
|
||||
private readonly Mock<IDelegatingHandlerHandlerProvider> _provider;
|
||||
private IHttpClientBuilder _builderResult;
|
||||
private IHttpClient _httpClient;
|
||||
private HttpResponseMessage _response;
|
||||
private Ocelot.Request.Request _request;
|
||||
|
||||
public HttpClientBuilderTests()
|
||||
{
|
||||
_provider = new Mock<IDelegatingHandlerHandlerProvider>();
|
||||
_house = new Mock<IDelegatingHandlerHandlerHouse>();
|
||||
_builder = new HttpClientBuilder(_house.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_build_http_client()
|
||||
{
|
||||
this.Given(x => GivenTheProviderReturns())
|
||||
.And(x => GivenARequest())
|
||||
.And(x => GivenTheHouseReturns())
|
||||
.When(x => WhenIBuild())
|
||||
.Then(x => ThenTheHttpClientShouldNotBeNull())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_call_delegating_handlers_in_order()
|
||||
{
|
||||
var fakeOne = new FakeDelegatingHandler();
|
||||
var fakeTwo = new FakeDelegatingHandler();
|
||||
|
||||
var handlers = new List<Func<DelegatingHandler>>()
|
||||
{
|
||||
() => fakeOne,
|
||||
() => fakeTwo
|
||||
};
|
||||
|
||||
this.Given(x => GivenTheProviderReturns(handlers))
|
||||
.And(x => GivenARequest())
|
||||
.And(x => GivenTheHouseReturns())
|
||||
.And(x => WhenIBuild())
|
||||
.When(x => WhenICallTheClient())
|
||||
.Then(x => ThenTheFakeAreHandledInOrder(fakeOne, fakeTwo))
|
||||
.And(x => ThenSomethingIsReturned())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenARequest()
|
||||
{
|
||||
_request = new Ocelot.Request.Request(null, false, null, false, false, "", false);
|
||||
}
|
||||
|
||||
private void GivenTheHouseReturns()
|
||||
{
|
||||
_house
|
||||
.Setup(x => x.Get(It.IsAny<Ocelot.Request.Request>()))
|
||||
.Returns(new OkResponse<IDelegatingHandlerHandlerProvider>(_provider.Object));
|
||||
}
|
||||
|
||||
private void ThenSomethingIsReturned()
|
||||
{
|
||||
_response.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
private void WhenICallTheClient()
|
||||
{
|
||||
_response = _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, "http://test.com")).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private void ThenTheFakeAreHandledInOrder(FakeDelegatingHandler fakeOne, FakeDelegatingHandler fakeTwo)
|
||||
{
|
||||
fakeOne.TimeCalled.ShouldBeGreaterThan(fakeTwo.TimeCalled);
|
||||
}
|
||||
|
||||
private void GivenTheProviderReturns()
|
||||
{
|
||||
_provider
|
||||
.Setup(x => x.Get())
|
||||
.Returns(new List<Func<DelegatingHandler>>(){ () => new FakeDelegatingHandler()});
|
||||
}
|
||||
|
||||
private void GivenTheProviderReturns(List<Func<DelegatingHandler>> handlers)
|
||||
{
|
||||
_provider
|
||||
.Setup(x => x.Get())
|
||||
.Returns(handlers);
|
||||
}
|
||||
|
||||
private void WhenIBuild()
|
||||
{
|
||||
_httpClient = _builder.Create(_request);
|
||||
}
|
||||
|
||||
private void ThenTheHttpClientShouldNotBeNull()
|
||||
{
|
||||
_httpClient.ShouldNotBeNull();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
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;
|
||||
using Shouldly;
|
||||
|
||||
namespace Ocelot.UnitTests.Requester
|
||||
{
|
||||
public class HttpClientHttpRequesterTest
|
||||
{
|
||||
private readonly Mock<IHttpClientCache> _cacheHandlers;
|
||||
private Mock<IDelegatingHandlerHandlerHouse> _house;
|
||||
private Mock<IDelegatingHandlerHandlerProvider> _provider;
|
||||
private Response<HttpResponseMessage> _response;
|
||||
private readonly HttpClientHttpRequester _httpClientRequester;
|
||||
private Ocelot.Request.Request _request;
|
||||
private Mock<IOcelotLoggerFactory> _loggerFactory;
|
||||
private Mock<IOcelotLogger> _logger;
|
||||
|
||||
public HttpClientHttpRequesterTest()
|
||||
{
|
||||
_provider = new Mock<IDelegatingHandlerHandlerProvider>();
|
||||
_provider.Setup(x => x.Get()).Returns(new List<Func<DelegatingHandler>>());
|
||||
_house = new Mock<IDelegatingHandlerHandlerHouse>();
|
||||
_house.Setup(x => x.Get(It.IsAny<Ocelot.Request.Request>())).Returns(new OkResponse<IDelegatingHandlerHandlerProvider>(_provider.Object));
|
||||
_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, _house.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_unable_to_complete_request()
|
||||
{
|
||||
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()
|
||||
{
|
||||
_response.IsError.ShouldBeFalse();
|
||||
}
|
||||
|
||||
private void ThenTheResponseIsCalledError()
|
||||
{
|
||||
_response.IsError.ShouldBeTrue();
|
||||
}
|
||||
}
|
||||
}
|
@ -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,8 @@
|
||||
using Moq;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.LoadBalancer.LoadBalancers;
|
||||
using Ocelot.Requester.QoS;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.UnitTests.LoadBalancer;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
@ -121,7 +121,7 @@ namespace Ocelot.UnitTests.Responder
|
||||
// If this test fails then it's because the number of error codes has changed.
|
||||
// You should make the appropriate changes to the test cases here to ensure
|
||||
// they cover all the error codes, and then modify this assertion.
|
||||
Enum.GetNames(typeof(OcelotErrorCode)).Length.ShouldBe(32, "Looks like the number of error codes has changed. Do you need to modify ErrorsToHttpStatusCodeMapper?");
|
||||
Enum.GetNames(typeof(OcelotErrorCode)).Length.ShouldBe(33, "Looks like the number of error codes has changed. Do you need to modify ErrorsToHttpStatusCodeMapper?");
|
||||
}
|
||||
|
||||
private void ShouldMapErrorToStatusCode(OcelotErrorCode errorCode, HttpStatusCode expectedHttpStatusCode)
|
||||
|
64
test/Ocelot.UnitTests/Responder/HttpContextResponderTests.cs
Normal file
64
test/Ocelot.UnitTests/Responder/HttpContextResponderTests.cs
Normal file
@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Headers;
|
||||
using Ocelot.Responder;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.Responder
|
||||
{
|
||||
public class HttpContextResponderTests
|
||||
{
|
||||
private readonly HttpContextResponder _responder;
|
||||
private RemoveOutputHeaders _removeOutputHeaders;
|
||||
|
||||
public HttpContextResponderTests()
|
||||
{
|
||||
_removeOutputHeaders = new RemoveOutputHeaders();
|
||||
_responder = new HttpContextResponder(_removeOutputHeaders);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_remove_transfer_encoding_header()
|
||||
{
|
||||
var httpContext = new DefaultHttpContext();
|
||||
var httpResponseMessage = new HttpResponseMessage {Content = new StringContent("")};
|
||||
httpResponseMessage.Headers.Add("Transfer-Encoding", "woop");
|
||||
_responder.SetResponseOnHttpContext(httpContext, httpResponseMessage).GetAwaiter().GetResult();
|
||||
var header = httpContext.Response.Headers["Transfer-Encoding"];
|
||||
header.ShouldBeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_have_content_length()
|
||||
{
|
||||
var httpContext = new DefaultHttpContext();
|
||||
var httpResponseMessage = new HttpResponseMessage { Content = new StringContent("test") };
|
||||
_responder.SetResponseOnHttpContext(httpContext, httpResponseMessage).GetAwaiter().GetResult();
|
||||
var header = httpContext.Response.Headers["Content-Length"];
|
||||
header.First().ShouldBe("4");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_add_header()
|
||||
{
|
||||
var httpContext = new DefaultHttpContext();
|
||||
var httpResponseMessage = new HttpResponseMessage { Content = new StringContent("test") };
|
||||
httpResponseMessage.Headers.Add("test", "test");
|
||||
_responder.SetResponseOnHttpContext(httpContext, httpResponseMessage).GetAwaiter().GetResult();
|
||||
var header = httpContext.Response.Headers["test"];
|
||||
header.First().ShouldBe("test");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_call_without_exception()
|
||||
{
|
||||
var httpContext = new DefaultHttpContext();
|
||||
_responder.SetErrorResponseOnContext(httpContext, 500);
|
||||
}
|
||||
}
|
||||
}
|
@ -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 }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,212 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Consul;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Moq;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.ServiceDiscovery;
|
||||
using Ocelot.Values;
|
||||
using Xunit;
|
||||
using TestStack.BDDfy;
|
||||
using Shouldly;
|
||||
|
||||
namespace Ocelot.UnitTests.ServiceDiscovery
|
||||
{
|
||||
public class ConsulServiceDiscoveryProviderTests : IDisposable
|
||||
{
|
||||
private IWebHost _fakeConsulBuilder;
|
||||
private readonly List<ServiceEntry> _serviceEntries;
|
||||
private readonly ConsulServiceDiscoveryProvider _provider;
|
||||
private readonly string _serviceName;
|
||||
private readonly int _port;
|
||||
private readonly string _consulHost;
|
||||
private readonly string _fakeConsulServiceDiscoveryUrl;
|
||||
private List<Service> _services;
|
||||
private Mock<IOcelotLoggerFactory> _factory;
|
||||
private readonly Mock<IOcelotLogger> _logger;
|
||||
|
||||
public ConsulServiceDiscoveryProviderTests()
|
||||
{
|
||||
_serviceName = "test";
|
||||
_port = 8500;
|
||||
_consulHost = "localhost";
|
||||
_fakeConsulServiceDiscoveryUrl = $"http://{_consulHost}:{_port}";
|
||||
_serviceEntries = new List<ServiceEntry>();
|
||||
|
||||
_factory = new Mock<IOcelotLoggerFactory>();
|
||||
_logger = new Mock<IOcelotLogger>();
|
||||
_factory.Setup(x => x.CreateLogger<ConsulServiceDiscoveryProvider>()).Returns(_logger.Object);
|
||||
|
||||
var config = new ConsulRegistryConfiguration(_consulHost, _port, _serviceName);
|
||||
_provider = new ConsulServiceDiscoveryProvider(config, _factory.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_service_from_consul()
|
||||
{
|
||||
var serviceEntryOne = new ServiceEntry()
|
||||
{
|
||||
Service = new AgentService()
|
||||
{
|
||||
Service = _serviceName,
|
||||
Address = "localhost",
|
||||
Port = 50881,
|
||||
ID = Guid.NewGuid().ToString(),
|
||||
Tags = new string[0]
|
||||
},
|
||||
};
|
||||
|
||||
this.Given(x =>GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName))
|
||||
.And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne))
|
||||
.When(x => WhenIGetTheServices())
|
||||
.Then(x => ThenTheCountIs(1))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_not_return_services_with_invalid_address()
|
||||
{
|
||||
var serviceEntryOne = new ServiceEntry()
|
||||
{
|
||||
Service = new AgentService()
|
||||
{
|
||||
Service = _serviceName,
|
||||
Address = "http://localhost",
|
||||
Port = 50881,
|
||||
ID = Guid.NewGuid().ToString(),
|
||||
Tags = new string[0]
|
||||
},
|
||||
};
|
||||
|
||||
var serviceEntryTwo = new ServiceEntry()
|
||||
{
|
||||
Service = new AgentService()
|
||||
{
|
||||
Service = _serviceName,
|
||||
Address = "http://localhost",
|
||||
Port = 50888,
|
||||
ID = Guid.NewGuid().ToString(),
|
||||
Tags = new string[0]
|
||||
},
|
||||
};
|
||||
|
||||
this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName))
|
||||
.And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo))
|
||||
.When(x => WhenIGetTheServices())
|
||||
.Then(x => ThenTheCountIs(0))
|
||||
.And(x => ThenTheLoggerHasBeenCalledCorrectlyForInvalidAddress())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_not_return_services_with_invalid_port()
|
||||
{
|
||||
var serviceEntryOne = new ServiceEntry()
|
||||
{
|
||||
Service = new AgentService()
|
||||
{
|
||||
Service = _serviceName,
|
||||
Address = "localhost",
|
||||
Port = -1,
|
||||
ID = Guid.NewGuid().ToString(),
|
||||
Tags = new string[0]
|
||||
},
|
||||
};
|
||||
|
||||
var serviceEntryTwo = new ServiceEntry()
|
||||
{
|
||||
Service = new AgentService()
|
||||
{
|
||||
Service = _serviceName,
|
||||
Address = "localhost",
|
||||
Port = 0,
|
||||
ID = Guid.NewGuid().ToString(),
|
||||
Tags = new string[0]
|
||||
},
|
||||
};
|
||||
|
||||
this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName))
|
||||
.And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo))
|
||||
.When(x => WhenIGetTheServices())
|
||||
.Then(x => ThenTheCountIs(0))
|
||||
.And(x => ThenTheLoggerHasBeenCalledCorrectlyForInvalidPorts())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenTheLoggerHasBeenCalledCorrectlyForInvalidAddress()
|
||||
{
|
||||
_logger.Verify(
|
||||
x => x.LogError(
|
||||
"Unable to use service Address: http://localhost and Port: 50881 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"),
|
||||
Times.Once);
|
||||
|
||||
_logger.Verify(
|
||||
x => x.LogError(
|
||||
"Unable to use service Address: http://localhost and Port: 50888 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"),
|
||||
Times.Once);
|
||||
}
|
||||
|
||||
private void ThenTheLoggerHasBeenCalledCorrectlyForInvalidPorts()
|
||||
{
|
||||
_logger.Verify(
|
||||
x => x.LogError(
|
||||
"Unable to use service Address: localhost and Port: -1 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"),
|
||||
Times.Once);
|
||||
|
||||
_logger.Verify(
|
||||
x => x.LogError(
|
||||
"Unable to use service Address: localhost and Port: 0 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"),
|
||||
Times.Once);
|
||||
}
|
||||
|
||||
private void ThenTheCountIs(int count)
|
||||
{
|
||||
_services.Count.ShouldBe(count);
|
||||
}
|
||||
|
||||
private void WhenIGetTheServices()
|
||||
{
|
||||
_services = _provider.Get().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private void GivenTheServicesAreRegisteredWithConsul(params ServiceEntry[] serviceEntries)
|
||||
{
|
||||
foreach (var serviceEntry in serviceEntries)
|
||||
{
|
||||
_serviceEntries.Add(serviceEntry);
|
||||
}
|
||||
}
|
||||
|
||||
private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string serviceName)
|
||||
{
|
||||
_fakeConsulBuilder = new WebHostBuilder()
|
||||
.UseUrls(url)
|
||||
.UseKestrel()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseIISIntegration()
|
||||
.UseUrls(url)
|
||||
.Configure(app =>
|
||||
{
|
||||
app.Run(async context =>
|
||||
{
|
||||
if (context.Request.Path.Value == $"/v1/health/service/{serviceName}")
|
||||
{
|
||||
await context.Response.WriteJsonAsync(_serviceEntries);
|
||||
}
|
||||
});
|
||||
})
|
||||
.Build();
|
||||
|
||||
_fakeConsulBuilder.Start();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_fakeConsulBuilder?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Moq;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.ServiceDiscovery;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
@ -15,10 +17,12 @@ namespace Ocelot.UnitTests.ServiceDiscovery
|
||||
private IServiceDiscoveryProvider _result;
|
||||
private readonly ServiceDiscoveryProviderFactory _factory;
|
||||
private ReRoute _reRoute;
|
||||
private Mock<IOcelotLoggerFactory> _loggerFactory;
|
||||
|
||||
public ServiceProviderFactoryTests()
|
||||
{
|
||||
_factory = new ServiceDiscoveryProviderFactory();
|
||||
_loggerFactory = new Mock<IOcelotLoggerFactory>();
|
||||
_factory = new ServiceDiscoveryProviderFactory(_loggerFactory.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -104,4 +108,4 @@ namespace Ocelot.UnitTests.ServiceDiscovery
|
||||
_result.ShouldBeOfType<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user