mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 06:22:50 +08:00
Merge branch 'release-3.1.7'
This commit is contained in:
commit
1aa077560c
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 15
|
# Visual Studio 15
|
||||||
VisualStudioVersion = 15.0.27130.2024
|
VisualStudioVersion = 15.0.27130.2036
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5CFB79B7-C9DC-45A4-9A75-625D92471702}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5CFB79B7-C9DC-45A4-9A75-625D92471702}"
|
||||||
EndProject
|
EndProject
|
||||||
|
BIN
docs/favicon.ico
BIN
docs/favicon.ico
Binary file not shown.
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 8.1 KiB |
@ -21,7 +21,7 @@ All you need to do to hook into your own IdentityServer is add the following to
|
|||||||
};
|
};
|
||||||
|
|
||||||
services
|
services
|
||||||
.AddOcelot(Configuration)
|
.AddOcelot()
|
||||||
.AddAdministration("/administration", options);
|
.AddAdministration("/administration", options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ The secret is the client secret that Ocelot's internal IdentityServer will use t
|
|||||||
public virtual void ConfigureServices(IServiceCollection services)
|
public virtual void ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
services
|
services
|
||||||
.AddOcelot(Configuration)
|
.AddOcelot()
|
||||||
.AddAdministration("/administration", "secret");
|
.AddAdministration("/administration", "secret");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ If you want to authenticate using JWT tokens maybe from a provider like Auth0 yo
|
|||||||
x.Audience = "test";
|
x.Audience = "test";
|
||||||
});
|
});
|
||||||
|
|
||||||
services.AddOcelot(Configuration);
|
services.AddOcelot();
|
||||||
}
|
}
|
||||||
|
|
||||||
Then map the authentication provider key to a ReRoute in your configuration e.g.
|
Then map the authentication provider key to a ReRoute in your configuration e.g.
|
||||||
@ -111,7 +111,7 @@ In order to use IdentityServer bearer tokens register your IdentityServer servic
|
|||||||
services.AddAuthentication()
|
services.AddAuthentication()
|
||||||
.AddIdentityServerAuthentication(authenticationProviderKey, options);
|
.AddIdentityServerAuthentication(authenticationProviderKey, options);
|
||||||
|
|
||||||
services.AddOcelot(Configuration);
|
services.AddOcelot();
|
||||||
}
|
}
|
||||||
|
|
||||||
Then map the authentication provider key to a ReRoute in your configuration e.g.
|
Then map the authentication provider key to a ReRoute in your configuration e.g.
|
||||||
|
@ -78,6 +78,29 @@ Set it true if the request should automatically follow redirection responses fro
|
|||||||
- _UseCookieContainer_ is a value that indicates whether the handler uses the CookieContainer property to store server cookies and uses these cookies when sending requests.
|
- _UseCookieContainer_ is a value that indicates whether the handler uses the CookieContainer property to store server cookies and uses these cookies when sending requests.
|
||||||
The default value is true.
|
The default value is true.
|
||||||
|
|
||||||
|
Multiple environments
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Like any other asp.net core project Ocelot supports configuration file names such as configuration.dev.json, configuration.test.json etc. In order to implement this add the following
|
||||||
|
to you
|
||||||
|
|
||||||
|
.. code-block:: csharp
|
||||||
|
|
||||||
|
.ConfigureAppConfiguration((hostingContext, config) =>
|
||||||
|
{
|
||||||
|
config
|
||||||
|
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
|
||||||
|
.AddJsonFile("appsettings.json", true, true)
|
||||||
|
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
||||||
|
.AddJsonFile("configuration.json")
|
||||||
|
.AddJsonFile($"configuration.{hostingContext.HostingEnvironment.EnvironmentName}.json")
|
||||||
|
.AddEnvironmentVariables();
|
||||||
|
})
|
||||||
|
|
||||||
|
Ocelot should now use the environment specific configuration and fall back to configuration.json if there isnt one.
|
||||||
|
|
||||||
|
You also need to set the corresponding environment variable which is ASPNETCORE_ENVIRONMENT. More info on this can be found in the `asp.net core docs <https://docs.microsoft.com/en-us/aspnet/core/fundamentals/environments>`_.
|
||||||
|
|
||||||
Store configuration in consul
|
Store configuration in consul
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
@ -86,7 +109,7 @@ If you add the following when you register your services Ocelot will attempt to
|
|||||||
.. code-block:: csharp
|
.. code-block:: csharp
|
||||||
|
|
||||||
services
|
services
|
||||||
.AddOcelot(Configuration)
|
.AddOcelot()
|
||||||
.AddStoreOcelotConfigurationInConsul();
|
.AddStoreOcelotConfigurationInConsul();
|
||||||
|
|
||||||
You also need to add the following to your configuration.json. This is how Ocelot
|
You also need to add the following to your configuration.json. This is how Ocelot
|
||||||
|
@ -12,7 +12,7 @@ In order to enable Rafty in Ocelot you must make the following changes to your S
|
|||||||
public virtual void ConfigureServices(IServiceCollection services)
|
public virtual void ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
services
|
services
|
||||||
.AddOcelot(Configuration)
|
.AddOcelot()
|
||||||
.AddAdministration("/administration", "secret")
|
.AddAdministration("/administration", "secret")
|
||||||
.AddRafty();
|
.AddRafty();
|
||||||
}
|
}
|
||||||
|
40
docs/features/ratelimiting.rst
Normal file
40
docs/features/ratelimiting.rst
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
Rate Limiting
|
||||||
|
=============
|
||||||
|
|
||||||
|
Thanks to `@catcherwong article <http://www.c-sharpcorner.com/article/building-api-gateway-using-ocelot-in-asp-net-core-rate-limiting-part-four/>`_ for inspiring me to finally write this documentation.
|
||||||
|
|
||||||
|
Ocelot supports rate limiting of upstream requests so that your downstream services do not become overloaded. This feature was added by @geffzhang on GitHub! Thanks very much.
|
||||||
|
|
||||||
|
OK so to get rate limiting working for a ReRoute you need to add the following json to it.
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
"RateLimitOptions": {
|
||||||
|
"ClientWhitelist": [],
|
||||||
|
"EnableRateLimiting": true,
|
||||||
|
"Period": "1s",
|
||||||
|
"PeriodTimespan": 1,
|
||||||
|
"Limit": 1
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientWhitelist - This is an array that contains the whitelist of the client. It means that the client in this array will not be affected by the rate limiting.
|
||||||
|
EnableRateLimiting - This value specifies enable endpoint rate limiting.
|
||||||
|
Period - This value specifies the period, such as 1s, 5m, 1h,1d and so on.
|
||||||
|
PeriodTimespan - This value specifies that we can retry after a certain number of seconds.
|
||||||
|
Limit - This value specifies the maximum number of requests that a client can make in a defined period.
|
||||||
|
|
||||||
|
You can also set the following in the GlobalConfiguration part of configuration.json
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
"RateLimitOptions": {
|
||||||
|
"DisableRateLimitHeaders": false,
|
||||||
|
"QuotaExceededMessage": "Customize Tips!",
|
||||||
|
"HttpStatusCode": 999,
|
||||||
|
"ClientIdHeader" : "Test"
|
||||||
|
}
|
||||||
|
|
||||||
|
DisableRateLimitHeaders - This value specifies whether X-Rate-Limit and Rety-After headers are disabled.
|
||||||
|
QuotaExceededMessage - This value specifies the exceeded message.
|
||||||
|
HttpStatusCode - This value specifies the returned HTTP Status code when rate limiting occurs.
|
||||||
|
ClientIdHeader - Allows you to specifiy the header that should be used to identify clients. By default it is "ClientId"
|
95
docs/features/requestaggregation.rst
Normal file
95
docs/features/requestaggregation.rst
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
Request Aggregation
|
||||||
|
===================
|
||||||
|
|
||||||
|
Ocelot allow's you to specify Aggregate ReRoutes that compose multiple normal ReRoutes and map their responses into one object. This is usual where you have
|
||||||
|
a client that is making multiple requests to a server where it could just be one. This feature allows you to start implementing back end for a front end type
|
||||||
|
architecture with Ocelot.
|
||||||
|
|
||||||
|
This feature was requested as part of `Issue 79 <https://github.com/TomPallister/Ocelot/pull/79>`_ .
|
||||||
|
|
||||||
|
In order to set this up you must do something like the following in your configuration.json. Here we have specified two normal ReRoutes and each one has a Key property.
|
||||||
|
We then specify an Aggregate that composes the two ReRoutes using their keys in the ReRouteKeys list and says then we have the UpstreamPathTemplate which works like a normal ReRoute.
|
||||||
|
Obviously you cannot have duplicate UpstreamPathTemplates between ReRoutes and Aggregates. You can use all of Ocelot's normal ReRoute options apart from RequestIdKey (explained in gotchas below).
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"ReRoutes": [
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/",
|
||||||
|
"UpstreamPathTemplate": "/laura",
|
||||||
|
"UpstreamHttpMethod": [
|
||||||
|
"Get"
|
||||||
|
],
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHostAndPorts": [
|
||||||
|
{
|
||||||
|
"Host": "localhost",
|
||||||
|
"Port": 51881
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Key": "Laura"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/",
|
||||||
|
"UpstreamPathTemplate": "/tom",
|
||||||
|
"UpstreamHttpMethod": [
|
||||||
|
"Get"
|
||||||
|
],
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHostAndPorts": [
|
||||||
|
{
|
||||||
|
"Host": "localhost",
|
||||||
|
"Port": 51882
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Key": "Tom"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Aggregates": [
|
||||||
|
{
|
||||||
|
"ReRouteKeys": [
|
||||||
|
"Tom",
|
||||||
|
"Laura"
|
||||||
|
],
|
||||||
|
"UpstreamPathTemplate": "/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
You can also set UpstreamHost and ReRouteIsCaseSensitive in the Aggregate configuration. These behave the same as any other ReRoutes.
|
||||||
|
|
||||||
|
If the ReRoute /tom returned a body of {"Age": 19} and /laura returned {"Age": 25} the the response after aggregation would be as follows.
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{"Tom":{"Age": 19},"Laura":{"Age": 25}}
|
||||||
|
|
||||||
|
Gotcha's / Further info
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
At the moment the aggregation is very simple. Ocelot just gets the response from your downstream service and sticks it into a json dictionary
|
||||||
|
as above. With the ReRoute key being the key of the dictionary and the value the response body from your downstream service. You can see that the object is just
|
||||||
|
JSON without any pretty spaces etc.
|
||||||
|
|
||||||
|
All headers will be lost from the downstream services response.
|
||||||
|
|
||||||
|
Ocelot will always return content type application/json with an aggregate request.
|
||||||
|
|
||||||
|
You cannot use ReRoutes with specific RequestIdKeys as this would be crazy complicated to track.
|
||||||
|
|
||||||
|
Aggregation only supports the GET HTTP Verb.
|
||||||
|
|
||||||
|
If you downstream services return a 404 the aggregate will just return nothing for that downstream service.
|
||||||
|
It will not change the aggregate response into a 404 even if all the downstreams return a 404.
|
||||||
|
|
||||||
|
Future
|
||||||
|
^^^^^^
|
||||||
|
|
||||||
|
There are loads of cool ways to enchance this such as..
|
||||||
|
|
||||||
|
What happens when downstream goes slow..should we timeout?
|
||||||
|
Can we do something like GraphQL where the user chooses what fields are returned?
|
||||||
|
Can we handle 404 better etc?
|
||||||
|
Can we make this not just support a JSON dictionary response?
|
||||||
|
|
@ -12,7 +12,7 @@ In your ConfigureServices method
|
|||||||
.. code-block:: csharp
|
.. code-block:: csharp
|
||||||
|
|
||||||
services
|
services
|
||||||
.AddOcelot(Configuration)
|
.AddOcelot()
|
||||||
.AddOpenTracing(option =>
|
.AddOpenTracing(option =>
|
||||||
{
|
{
|
||||||
//this is the url that the butterfly collector server is running on...
|
//this is the url that the butterfly collector server is running on...
|
||||||
|
@ -18,13 +18,14 @@ Thanks for taking a look at the Ocelot documentation. Please use the left hand n
|
|||||||
:hidden:
|
:hidden:
|
||||||
:caption: Features
|
:caption: Features
|
||||||
|
|
||||||
features/routing
|
|
||||||
features/configuration
|
features/configuration
|
||||||
|
features/routing
|
||||||
|
features/requestaggregation
|
||||||
features/servicediscovery
|
features/servicediscovery
|
||||||
features/authentication
|
features/authentication
|
||||||
features/authorisation
|
features/authorisation
|
||||||
features/administration
|
features/administration
|
||||||
features/raft
|
features/ratelimiting
|
||||||
features/caching
|
features/caching
|
||||||
features/qualityofservice
|
features/qualityofservice
|
||||||
features/headerstransformation
|
features/headerstransformation
|
||||||
@ -35,7 +36,7 @@ Thanks for taking a look at the Ocelot documentation. Please use the left hand n
|
|||||||
features/middlewareinjection
|
features/middlewareinjection
|
||||||
features/loadbalancer
|
features/loadbalancer
|
||||||
features/delegatinghandlers
|
features/delegatinghandlers
|
||||||
|
features/raft
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Ocelot.Configuration;
|
using Ocelot.Configuration;
|
||||||
using Ocelot.Errors;
|
using Ocelot.Errors;
|
||||||
using Ocelot.Infrastructure.Extensions;
|
using Ocelot.Infrastructure.Extensions;
|
||||||
using Ocelot.Infrastructure.RequestData;
|
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
|
|
||||||
@ -14,35 +11,29 @@ namespace Ocelot.Authentication.Middleware
|
|||||||
{
|
{
|
||||||
public class AuthenticationMiddleware : OcelotMiddleware
|
public class AuthenticationMiddleware : OcelotMiddleware
|
||||||
{
|
{
|
||||||
private readonly RequestDelegate _next;
|
private readonly OcelotRequestDelegate _next;
|
||||||
private readonly IApplicationBuilder _app;
|
|
||||||
private readonly IAuthenticationSchemeProvider _authSchemeProvider;
|
|
||||||
private readonly IOcelotLogger _logger;
|
private readonly IOcelotLogger _logger;
|
||||||
|
|
||||||
public AuthenticationMiddleware(RequestDelegate next,
|
public AuthenticationMiddleware(OcelotRequestDelegate next,
|
||||||
IApplicationBuilder app,
|
|
||||||
IRequestScopedDataRepository requestScopedDataRepository,
|
|
||||||
IOcelotLoggerFactory loggerFactory)
|
IOcelotLoggerFactory loggerFactory)
|
||||||
: base(requestScopedDataRepository)
|
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_app = app;
|
|
||||||
_logger = loggerFactory.CreateLogger<AuthenticationMiddleware>();
|
_logger = loggerFactory.CreateLogger<AuthenticationMiddleware>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Invoke(HttpContext context)
|
public async Task Invoke(DownstreamContext context)
|
||||||
{
|
{
|
||||||
if (IsAuthenticatedRoute(DownstreamRoute.ReRoute))
|
if (IsAuthenticatedRoute(context.DownstreamReRoute))
|
||||||
{
|
{
|
||||||
_logger.LogDebug($"{context.Request.Path} is an authenticated route. {MiddlewareName} checking if client is authenticated");
|
_logger.LogDebug($"{context.HttpContext.Request.Path} is an authenticated route. {MiddlewareName} checking if client is authenticated");
|
||||||
|
|
||||||
var result = await context.AuthenticateAsync(DownstreamRoute.ReRoute.AuthenticationOptions.AuthenticationProviderKey);
|
var result = await context.HttpContext.AuthenticateAsync(context.DownstreamReRoute.AuthenticationOptions.AuthenticationProviderKey);
|
||||||
|
|
||||||
context.User = result.Principal;
|
context.HttpContext.User = result.Principal;
|
||||||
|
|
||||||
if (context.User.Identity.IsAuthenticated)
|
if (context.HttpContext.User.Identity.IsAuthenticated)
|
||||||
{
|
{
|
||||||
_logger.LogDebug($"Client has been authenticated for {context.Request.Path}");
|
_logger.LogDebug($"Client has been authenticated for {context.HttpContext.Request.Path}");
|
||||||
await _next.Invoke(context);
|
await _next.Invoke(context);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -50,22 +41,23 @@ namespace Ocelot.Authentication.Middleware
|
|||||||
var error = new List<Error>
|
var error = new List<Error>
|
||||||
{
|
{
|
||||||
new UnauthenticatedError(
|
new UnauthenticatedError(
|
||||||
$"Request for authenticated route {context.Request.Path} by {context.User.Identity.Name} was unauthenticated")
|
$"Request for authenticated route {context.HttpContext.Request.Path} by {context.HttpContext.User.Identity.Name} was unauthenticated")
|
||||||
};
|
};
|
||||||
|
|
||||||
_logger.LogError($"Client has NOT been authenticated for {context.Request.Path} and pipeline error set. {error.ToErrorString()}");
|
_logger.LogError($"Client has NOT been authenticated for {context.HttpContext.Request.Path} and pipeline error set. {error.ToErrorString()}");
|
||||||
SetPipelineError(error);
|
|
||||||
|
SetPipelineError(context, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.LogTrace($"No authentication needed for {context.Request.Path}");
|
_logger.LogTrace($"No authentication needed for {context.HttpContext.Request.Path}");
|
||||||
|
|
||||||
await _next.Invoke(context);
|
await _next.Invoke(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsAuthenticatedRoute(ReRoute reRoute)
|
private static bool IsAuthenticatedRoute(DownstreamReRoute reRoute)
|
||||||
{
|
{
|
||||||
return reRoute.IsAuthenticated;
|
return reRoute.IsAuthenticated;
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Ocelot.Middleware.Pipeline;
|
||||||
|
|
||||||
namespace Ocelot.Authentication.Middleware
|
namespace Ocelot.Authentication.Middleware
|
||||||
{
|
{
|
||||||
public static class AuthenticationMiddlewareMiddlewareExtensions
|
public static class AuthenticationMiddlewareMiddlewareExtensions
|
||||||
{
|
{
|
||||||
public static IApplicationBuilder UseAuthenticationMiddleware(this IApplicationBuilder builder)
|
public static IOcelotPipelineBuilder UseAuthenticationMiddleware(this IOcelotPipelineBuilder builder)
|
||||||
{
|
{
|
||||||
return builder.UseMiddleware<AuthenticationMiddleware>(builder);
|
return builder.UseMiddleware<AuthenticationMiddleware>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,21 +9,20 @@ namespace Ocelot.Authorisation.Middleware
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Errors;
|
using Errors;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
|
|
||||||
public class AuthorisationMiddleware : OcelotMiddleware
|
public class AuthorisationMiddleware : OcelotMiddleware
|
||||||
{
|
{
|
||||||
private readonly RequestDelegate _next;
|
private readonly OcelotRequestDelegate _next;
|
||||||
private readonly IClaimsAuthoriser _claimsAuthoriser;
|
private readonly IClaimsAuthoriser _claimsAuthoriser;
|
||||||
private readonly IScopesAuthoriser _scopesAuthoriser;
|
private readonly IScopesAuthoriser _scopesAuthoriser;
|
||||||
private readonly IOcelotLogger _logger;
|
private readonly IOcelotLogger _logger;
|
||||||
|
|
||||||
public AuthorisationMiddleware(RequestDelegate next,
|
public AuthorisationMiddleware(OcelotRequestDelegate next,
|
||||||
IRequestScopedDataRepository requestScopedDataRepository,
|
|
||||||
IClaimsAuthoriser claimsAuthoriser,
|
IClaimsAuthoriser claimsAuthoriser,
|
||||||
IScopesAuthoriser scopesAuthoriser,
|
IScopesAuthoriser scopesAuthoriser,
|
||||||
IOcelotLoggerFactory loggerFactory)
|
IOcelotLoggerFactory loggerFactory)
|
||||||
: base(requestScopedDataRepository)
|
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_claimsAuthoriser = claimsAuthoriser;
|
_claimsAuthoriser = claimsAuthoriser;
|
||||||
@ -31,19 +30,19 @@ namespace Ocelot.Authorisation.Middleware
|
|||||||
_logger = loggerFactory.CreateLogger<AuthorisationMiddleware>();
|
_logger = loggerFactory.CreateLogger<AuthorisationMiddleware>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Invoke(HttpContext context)
|
public async Task Invoke(DownstreamContext context)
|
||||||
{
|
{
|
||||||
if (IsAuthenticatedRoute(DownstreamRoute.ReRoute))
|
if (IsAuthenticatedRoute(context.DownstreamReRoute))
|
||||||
{
|
{
|
||||||
_logger.LogDebug("route is authenticated scopes must be checked");
|
_logger.LogDebug("route is authenticated scopes must be checked");
|
||||||
|
|
||||||
var authorised = _scopesAuthoriser.Authorise(context.User, DownstreamRoute.ReRoute.AuthenticationOptions.AllowedScopes);
|
var authorised = _scopesAuthoriser.Authorise(context.HttpContext.User, context.DownstreamReRoute.AuthenticationOptions.AllowedScopes);
|
||||||
|
|
||||||
if (authorised.IsError)
|
if (authorised.IsError)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("error authorising user scopes");
|
_logger.LogDebug("error authorising user scopes");
|
||||||
|
|
||||||
SetPipelineError(authorised.Errors);
|
SetPipelineError(context, authorised.Errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,46 +54,46 @@ namespace Ocelot.Authorisation.Middleware
|
|||||||
{
|
{
|
||||||
_logger.LogDebug("user scopes is not authorised setting pipeline error");
|
_logger.LogDebug("user scopes is not authorised setting pipeline error");
|
||||||
|
|
||||||
SetPipelineError(new List<Error>
|
SetPipelineError(context, new List<Error>
|
||||||
{
|
{
|
||||||
new UnauthorisedError(
|
new UnauthorisedError(
|
||||||
$"{context.User.Identity.Name} unable to access {DownstreamRoute.ReRoute.UpstreamPathTemplate.Value}")
|
$"{context.HttpContext.User.Identity.Name} unable to access {context.DownstreamReRoute.UpstreamPathTemplate.Value}")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsAuthorisedRoute(DownstreamRoute.ReRoute))
|
if (IsAuthorisedRoute(context.DownstreamReRoute))
|
||||||
{
|
{
|
||||||
_logger.LogDebug("route is authorised");
|
_logger.LogDebug("route is authorised");
|
||||||
|
|
||||||
var authorised = _claimsAuthoriser.Authorise(context.User, DownstreamRoute.ReRoute.RouteClaimsRequirement);
|
var authorised = _claimsAuthoriser.Authorise(context.HttpContext.User, context.DownstreamReRoute.RouteClaimsRequirement);
|
||||||
|
|
||||||
if (authorised.IsError)
|
if (authorised.IsError)
|
||||||
{
|
{
|
||||||
_logger.LogDebug($"Error whilst authorising {context.User.Identity.Name} for {context.User.Identity.Name}. Setting pipeline error");
|
_logger.LogDebug($"Error whilst authorising {context.HttpContext.User.Identity.Name} for {context.HttpContext.User.Identity.Name}. Setting pipeline error");
|
||||||
|
|
||||||
SetPipelineError(authorised.Errors);
|
SetPipelineError(context, authorised.Errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsAuthorised(authorised))
|
if (IsAuthorised(authorised))
|
||||||
{
|
{
|
||||||
_logger.LogDebug($"{context.User.Identity.Name} has succesfully been authorised for {DownstreamRoute.ReRoute.UpstreamPathTemplate.Value}. Calling next middleware");
|
_logger.LogDebug($"{context.HttpContext.User.Identity.Name} has succesfully been authorised for {context.DownstreamReRoute.UpstreamPathTemplate.Value}. Calling next middleware");
|
||||||
await _next.Invoke(context);
|
await _next.Invoke(context);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.LogDebug($"{context.User.Identity.Name} is not authorised to access {DownstreamRoute.ReRoute.UpstreamPathTemplate.Value}. Setting pipeline error");
|
_logger.LogDebug($"{context.HttpContext.User.Identity.Name} is not authorised to access {context.DownstreamReRoute.UpstreamPathTemplate.Value}. Setting pipeline error");
|
||||||
|
|
||||||
SetPipelineError(new List<Error>
|
SetPipelineError(context, new List<Error>
|
||||||
{
|
{
|
||||||
new UnauthorisedError($"{context.User.Identity.Name} is not authorised to access {DownstreamRoute.ReRoute.UpstreamPathTemplate.Value}")
|
new UnauthorisedError($"{context.HttpContext.User.Identity.Name} is not authorised to access {context.DownstreamReRoute.UpstreamPathTemplate.Value}")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.LogDebug($"{DownstreamRoute.ReRoute.DownstreamPathTemplate.Value} route does not require user to be authorised");
|
_logger.LogDebug($"{context.DownstreamReRoute.DownstreamPathTemplate.Value} route does not require user to be authorised");
|
||||||
await _next.Invoke(context);
|
await _next.Invoke(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,12 +103,12 @@ namespace Ocelot.Authorisation.Middleware
|
|||||||
return authorised.Data;
|
return authorised.Data;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsAuthenticatedRoute(ReRoute reRoute)
|
private static bool IsAuthenticatedRoute(DownstreamReRoute reRoute)
|
||||||
{
|
{
|
||||||
return reRoute.IsAuthenticated;
|
return reRoute.IsAuthenticated;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsAuthorisedRoute(ReRoute reRoute)
|
private static bool IsAuthorisedRoute(DownstreamReRoute reRoute)
|
||||||
{
|
{
|
||||||
return reRoute.IsAuthorised;
|
return reRoute.IsAuthorised;
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
|
using Ocelot.Middleware.Pipeline;
|
||||||
|
|
||||||
namespace Ocelot.Authorisation.Middleware
|
namespace Ocelot.Authorisation.Middleware
|
||||||
{
|
{
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
|
||||||
public static class AuthorisationMiddlewareMiddlewareExtensions
|
public static class AuthorisationMiddlewareMiddlewareExtensions
|
||||||
{
|
{
|
||||||
public static IApplicationBuilder UseAuthorisationMiddleware(this IApplicationBuilder builder)
|
public static IOcelotPipelineBuilder UseAuthorisationMiddleware(this IOcelotPipelineBuilder builder)
|
||||||
{
|
{
|
||||||
return builder.UseMiddleware<AuthorisationMiddleware>();
|
return builder.UseMiddleware<AuthorisationMiddleware>();
|
||||||
}
|
}
|
||||||
|
@ -7,22 +7,21 @@ using Ocelot.Infrastructure.RequestData;
|
|||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||||
|
|
||||||
namespace Ocelot.Cache.Middleware
|
namespace Ocelot.Cache.Middleware
|
||||||
{
|
{
|
||||||
public class OutputCacheMiddleware : OcelotMiddleware
|
public class OutputCacheMiddleware : OcelotMiddleware
|
||||||
{
|
{
|
||||||
private readonly RequestDelegate _next;
|
private readonly OcelotRequestDelegate _next;
|
||||||
private readonly IOcelotLogger _logger;
|
private readonly IOcelotLogger _logger;
|
||||||
private readonly IOcelotCache<CachedResponse> _outputCache;
|
private readonly IOcelotCache<CachedResponse> _outputCache;
|
||||||
private readonly IRegionCreator _regionCreator;
|
private readonly IRegionCreator _regionCreator;
|
||||||
|
|
||||||
public OutputCacheMiddleware(RequestDelegate next,
|
public OutputCacheMiddleware(OcelotRequestDelegate next,
|
||||||
IOcelotLoggerFactory loggerFactory,
|
IOcelotLoggerFactory loggerFactory,
|
||||||
IRequestScopedDataRepository scopedDataRepository,
|
|
||||||
IOcelotCache<CachedResponse> outputCache,
|
IOcelotCache<CachedResponse> outputCache,
|
||||||
IRegionCreator regionCreator)
|
IRegionCreator regionCreator)
|
||||||
: base(scopedDataRepository)
|
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_outputCache = outputCache;
|
_outputCache = outputCache;
|
||||||
@ -30,26 +29,26 @@ namespace Ocelot.Cache.Middleware
|
|||||||
_regionCreator = regionCreator;
|
_regionCreator = regionCreator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Invoke(HttpContext context)
|
public async Task Invoke(DownstreamContext context)
|
||||||
{
|
{
|
||||||
if (!DownstreamRoute.ReRoute.IsCached)
|
if (!context.DownstreamReRoute.IsCached)
|
||||||
{
|
{
|
||||||
await _next.Invoke(context);
|
await _next.Invoke(context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var downstreamUrlKey = $"{DownstreamRequest.Method.Method}-{DownstreamRequest.RequestUri.OriginalString}";
|
var downstreamUrlKey = $"{context.DownstreamRequest.Method.Method}-{context.DownstreamRequest.RequestUri.OriginalString}";
|
||||||
|
|
||||||
_logger.LogDebug("started checking cache for {downstreamUrlKey}", downstreamUrlKey);
|
_logger.LogDebug("started checking cache for {downstreamUrlKey}", downstreamUrlKey);
|
||||||
|
|
||||||
var cached = _outputCache.Get(downstreamUrlKey, DownstreamRoute.ReRoute.CacheOptions.Region);
|
var cached = _outputCache.Get(downstreamUrlKey, context.DownstreamReRoute.CacheOptions.Region);
|
||||||
|
|
||||||
if (cached != null)
|
if (cached != null)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("cache entry exists for {downstreamUrlKey}", downstreamUrlKey);
|
_logger.LogDebug("cache entry exists for {downstreamUrlKey}", downstreamUrlKey);
|
||||||
|
|
||||||
var response = CreateHttpResponseMessage(cached);
|
var response = CreateHttpResponseMessage(cached);
|
||||||
SetHttpResponseMessageThisRequest(response);
|
SetHttpResponseMessageThisRequest(context, response);
|
||||||
|
|
||||||
_logger.LogDebug("finished returned cached response for {downstreamUrlKey}", downstreamUrlKey);
|
_logger.LogDebug("finished returned cached response for {downstreamUrlKey}", downstreamUrlKey);
|
||||||
|
|
||||||
@ -60,20 +59,25 @@ namespace Ocelot.Cache.Middleware
|
|||||||
|
|
||||||
await _next.Invoke(context);
|
await _next.Invoke(context);
|
||||||
|
|
||||||
if (PipelineError)
|
if (context.IsError)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("there was a pipeline error for {downstreamUrlKey}", downstreamUrlKey);
|
_logger.LogDebug("there was a pipeline error for {downstreamUrlKey}", downstreamUrlKey);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cached = await CreateCachedResponse(HttpResponseMessage);
|
cached = await CreateCachedResponse(context.DownstreamResponse);
|
||||||
|
|
||||||
_outputCache.Add(downstreamUrlKey, cached, TimeSpan.FromSeconds(DownstreamRoute.ReRoute.CacheOptions.TtlSeconds), DownstreamRoute.ReRoute.CacheOptions.Region);
|
_outputCache.Add(downstreamUrlKey, cached, TimeSpan.FromSeconds(context.DownstreamReRoute.CacheOptions.TtlSeconds), context.DownstreamReRoute.CacheOptions.Region);
|
||||||
|
|
||||||
_logger.LogDebug("finished response added to cache for {downstreamUrlKey}", downstreamUrlKey);
|
_logger.LogDebug("finished response added to cache for {downstreamUrlKey}", downstreamUrlKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SetHttpResponseMessageThisRequest(DownstreamContext context, HttpResponseMessage response)
|
||||||
|
{
|
||||||
|
context.DownstreamResponse = response;
|
||||||
|
}
|
||||||
|
|
||||||
internal HttpResponseMessage CreateHttpResponseMessage(CachedResponse cached)
|
internal HttpResponseMessage CreateHttpResponseMessage(CachedResponse cached)
|
||||||
{
|
{
|
||||||
if (cached == null)
|
if (cached == null)
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Ocelot.Middleware.Pipeline;
|
||||||
|
|
||||||
namespace Ocelot.Cache.Middleware
|
namespace Ocelot.Cache.Middleware
|
||||||
{
|
{
|
||||||
public static class OutputCacheMiddlewareExtensions
|
public static class OutputCacheMiddlewareExtensions
|
||||||
{
|
{
|
||||||
public static IApplicationBuilder UseOutputCacheMiddleware(this IApplicationBuilder builder)
|
public static IOcelotPipelineBuilder UseOutputCacheMiddleware(this IOcelotPipelineBuilder builder)
|
||||||
{
|
{
|
||||||
return builder.UseMiddleware<OutputCacheMiddleware>();
|
return builder.UseMiddleware<OutputCacheMiddleware>();
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||||
using Ocelot.Infrastructure.RequestData;
|
using Ocelot.Infrastructure.RequestData;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
@ -9,34 +10,32 @@ namespace Ocelot.Claims.Middleware
|
|||||||
{
|
{
|
||||||
public class ClaimsBuilderMiddleware : OcelotMiddleware
|
public class ClaimsBuilderMiddleware : OcelotMiddleware
|
||||||
{
|
{
|
||||||
private readonly RequestDelegate _next;
|
private readonly OcelotRequestDelegate _next;
|
||||||
private readonly IAddClaimsToRequest _addClaimsToRequest;
|
private readonly IAddClaimsToRequest _addClaimsToRequest;
|
||||||
private readonly IOcelotLogger _logger;
|
private readonly IOcelotLogger _logger;
|
||||||
|
|
||||||
public ClaimsBuilderMiddleware(RequestDelegate next,
|
public ClaimsBuilderMiddleware(OcelotRequestDelegate next,
|
||||||
IRequestScopedDataRepository requestScopedDataRepository,
|
|
||||||
IOcelotLoggerFactory loggerFactory,
|
IOcelotLoggerFactory loggerFactory,
|
||||||
IAddClaimsToRequest addClaimsToRequest)
|
IAddClaimsToRequest addClaimsToRequest)
|
||||||
: base(requestScopedDataRepository)
|
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_addClaimsToRequest = addClaimsToRequest;
|
_addClaimsToRequest = addClaimsToRequest;
|
||||||
_logger = loggerFactory.CreateLogger<ClaimsBuilderMiddleware>();
|
_logger = loggerFactory.CreateLogger<ClaimsBuilderMiddleware>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Invoke(HttpContext context)
|
public async Task Invoke(DownstreamContext context)
|
||||||
{
|
{
|
||||||
if (DownstreamRoute.ReRoute.ClaimsToClaims.Any())
|
if (context.DownstreamReRoute.ClaimsToClaims.Any())
|
||||||
{
|
{
|
||||||
_logger.LogDebug("this route has instructions to convert claims to other claims");
|
_logger.LogDebug("this route has instructions to convert claims to other claims");
|
||||||
|
|
||||||
var result = _addClaimsToRequest.SetClaimsOnContext(DownstreamRoute.ReRoute.ClaimsToClaims, context);
|
var result = _addClaimsToRequest.SetClaimsOnContext(context.DownstreamReRoute.ClaimsToClaims, context.HttpContext);
|
||||||
|
|
||||||
if (result.IsError)
|
if (result.IsError)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("error converting claims to other claims, setting pipeline error");
|
_logger.LogDebug("error converting claims to other claims, setting pipeline error");
|
||||||
|
|
||||||
SetPipelineError(result.Errors);
|
SetPipelineError(context, result.Errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Ocelot.Middleware.Pipeline;
|
||||||
|
|
||||||
namespace Ocelot.Claims.Middleware
|
namespace Ocelot.Claims.Middleware
|
||||||
{
|
{
|
||||||
public static class ClaimsBuilderMiddlewareExtensions
|
public static class ClaimsBuilderMiddlewareExtensions
|
||||||
{
|
{
|
||||||
public static IApplicationBuilder UseClaimsBuilderMiddleware(this IApplicationBuilder builder)
|
public static IOcelotPipelineBuilder UseClaimsBuilderMiddleware(this IOcelotPipelineBuilder builder)
|
||||||
{
|
{
|
||||||
return builder.UseMiddleware<ClaimsBuilderMiddleware>();
|
return builder.UseMiddleware<ClaimsBuilderMiddleware>();
|
||||||
}
|
}
|
||||||
|
251
src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs
Normal file
251
src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Http;
|
||||||
|
using Ocelot.Values;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Ocelot.Configuration.Builder
|
||||||
|
{
|
||||||
|
public class DownstreamReRouteBuilder
|
||||||
|
{
|
||||||
|
private AuthenticationOptions _authenticationOptions;
|
||||||
|
private string _reRouteKey;
|
||||||
|
private string _downstreamPathTemplate;
|
||||||
|
private string _upstreamTemplate;
|
||||||
|
private UpstreamPathTemplate _upstreamTemplatePattern;
|
||||||
|
private List<HttpMethod> _upstreamHttpMethod;
|
||||||
|
private bool _isAuthenticated;
|
||||||
|
private List<ClaimToThing> _claimsToHeaders;
|
||||||
|
private List<ClaimToThing> _claimToClaims;
|
||||||
|
private Dictionary<string, string> _routeClaimRequirement;
|
||||||
|
private bool _isAuthorised;
|
||||||
|
private List<ClaimToThing> _claimToQueries;
|
||||||
|
private string _requestIdHeaderKey;
|
||||||
|
private bool _isCached;
|
||||||
|
private CacheOptions _fileCacheOptions;
|
||||||
|
private string _downstreamScheme;
|
||||||
|
private string _loadBalancer;
|
||||||
|
private bool _useQos;
|
||||||
|
private QoSOptions _qosOptions;
|
||||||
|
private HttpHandlerOptions _httpHandlerOptions;
|
||||||
|
private bool _enableRateLimiting;
|
||||||
|
private RateLimitOptions _rateLimitOptions;
|
||||||
|
private bool _useServiceDiscovery;
|
||||||
|
private string _serviceName;
|
||||||
|
private List<HeaderFindAndReplace> _upstreamHeaderFindAndReplace;
|
||||||
|
private List<HeaderFindAndReplace> _downstreamHeaderFindAndReplace;
|
||||||
|
private readonly List<DownstreamHostAndPort> _downstreamAddresses;
|
||||||
|
private string _upstreamHost;
|
||||||
|
private string _key;
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder()
|
||||||
|
{
|
||||||
|
_downstreamAddresses = new List<DownstreamHostAndPort>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithDownstreamAddresses(List<DownstreamHostAndPort> downstreamAddresses)
|
||||||
|
{
|
||||||
|
_downstreamAddresses.AddRange(downstreamAddresses);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithUpstreamHost(string upstreamAddresses)
|
||||||
|
{
|
||||||
|
_upstreamHost = upstreamAddresses;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithLoadBalancer(string loadBalancer)
|
||||||
|
{
|
||||||
|
_loadBalancer = loadBalancer;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithDownstreamScheme(string downstreamScheme)
|
||||||
|
{
|
||||||
|
_downstreamScheme = downstreamScheme;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithDownstreamPathTemplate(string input)
|
||||||
|
{
|
||||||
|
_downstreamPathTemplate = input;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithUpstreamPathTemplate(string input)
|
||||||
|
{
|
||||||
|
_upstreamTemplate = input;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithUpstreamTemplatePattern(UpstreamPathTemplate input)
|
||||||
|
{
|
||||||
|
_upstreamTemplatePattern = input;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithUpstreamHttpMethod(List<string> input)
|
||||||
|
{
|
||||||
|
_upstreamHttpMethod = (input.Count == 0) ? new List<HttpMethod>() : input.Select(x => new HttpMethod(x.Trim())).ToList();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithIsAuthenticated(bool input)
|
||||||
|
{
|
||||||
|
_isAuthenticated = input;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithIsAuthorised(bool input)
|
||||||
|
{
|
||||||
|
_isAuthorised = input;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithRequestIdKey(string input)
|
||||||
|
{
|
||||||
|
_requestIdHeaderKey = input;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithClaimsToHeaders(List<ClaimToThing> input)
|
||||||
|
{
|
||||||
|
_claimsToHeaders = input;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithClaimsToClaims(List<ClaimToThing> input)
|
||||||
|
{
|
||||||
|
_claimToClaims = input;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithRouteClaimsRequirement(Dictionary<string, string> input)
|
||||||
|
{
|
||||||
|
_routeClaimRequirement = input;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithClaimsToQueries(List<ClaimToThing> input)
|
||||||
|
{
|
||||||
|
_claimToQueries = input;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithIsCached(bool input)
|
||||||
|
{
|
||||||
|
_isCached = input;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithCacheOptions(CacheOptions input)
|
||||||
|
{
|
||||||
|
_fileCacheOptions = input;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithIsQos(bool input)
|
||||||
|
{
|
||||||
|
_useQos = input;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithQosOptions(QoSOptions input)
|
||||||
|
{
|
||||||
|
_qosOptions = input;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithReRouteKey(string reRouteKey)
|
||||||
|
{
|
||||||
|
_reRouteKey = reRouteKey;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithAuthenticationOptions(AuthenticationOptions authenticationOptions)
|
||||||
|
{
|
||||||
|
_authenticationOptions = authenticationOptions;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithEnableRateLimiting(bool input)
|
||||||
|
{
|
||||||
|
_enableRateLimiting = input;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithRateLimitOptions(RateLimitOptions input)
|
||||||
|
{
|
||||||
|
_rateLimitOptions = input;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithHttpHandlerOptions(HttpHandlerOptions input)
|
||||||
|
{
|
||||||
|
_httpHandlerOptions = input;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithUseServiceDiscovery(bool useServiceDiscovery)
|
||||||
|
{
|
||||||
|
_useServiceDiscovery = useServiceDiscovery;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithServiceName(string serviceName)
|
||||||
|
{
|
||||||
|
_serviceName = serviceName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithUpstreamHeaderFindAndReplace(List<HeaderFindAndReplace> upstreamHeaderFindAndReplace)
|
||||||
|
{
|
||||||
|
_upstreamHeaderFindAndReplace = upstreamHeaderFindAndReplace;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithDownstreamHeaderFindAndReplace(List<HeaderFindAndReplace> downstreamHeaderFindAndReplace)
|
||||||
|
{
|
||||||
|
_downstreamHeaderFindAndReplace = downstreamHeaderFindAndReplace;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithKey(string key)
|
||||||
|
{
|
||||||
|
_key = key;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public DownstreamReRoute Build()
|
||||||
|
{
|
||||||
|
return new DownstreamReRoute(
|
||||||
|
_key,
|
||||||
|
new PathTemplate(_upstreamTemplate),
|
||||||
|
_upstreamHeaderFindAndReplace,
|
||||||
|
_downstreamHeaderFindAndReplace,
|
||||||
|
_downstreamAddresses,
|
||||||
|
_serviceName,
|
||||||
|
_httpHandlerOptions,
|
||||||
|
_useServiceDiscovery,
|
||||||
|
_enableRateLimiting,
|
||||||
|
_useQos,
|
||||||
|
_qosOptions,
|
||||||
|
_downstreamScheme,
|
||||||
|
_requestIdHeaderKey,
|
||||||
|
_isCached,
|
||||||
|
_fileCacheOptions,
|
||||||
|
_loadBalancer,
|
||||||
|
_rateLimitOptions,
|
||||||
|
_routeClaimRequirement,
|
||||||
|
_claimToQueries,
|
||||||
|
_claimsToHeaders,
|
||||||
|
_claimToClaims,
|
||||||
|
_isAuthenticated,
|
||||||
|
_isAuthorised,
|
||||||
|
_authenticationOptions,
|
||||||
|
new PathTemplate(_downstreamPathTemplate),
|
||||||
|
_reRouteKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,43 +9,26 @@ namespace Ocelot.Configuration.Builder
|
|||||||
{
|
{
|
||||||
public class ReRouteBuilder
|
public class ReRouteBuilder
|
||||||
{
|
{
|
||||||
private AuthenticationOptions _authenticationOptions;
|
|
||||||
private string _reRouteKey;
|
|
||||||
private string _downstreamPathTemplate;
|
|
||||||
private string _upstreamTemplate;
|
private string _upstreamTemplate;
|
||||||
private UpstreamPathTemplate _upstreamTemplatePattern;
|
private UpstreamPathTemplate _upstreamTemplatePattern;
|
||||||
private List<HttpMethod> _upstreamHttpMethod;
|
private List<HttpMethod> _upstreamHttpMethod;
|
||||||
private bool _isAuthenticated;
|
|
||||||
private List<ClaimToThing> _configHeaderExtractorProperties;
|
|
||||||
private List<ClaimToThing> _claimToClaims;
|
|
||||||
private Dictionary<string, string> _routeClaimRequirement;
|
|
||||||
private bool _isAuthorised;
|
|
||||||
private List<ClaimToThing> _claimToQueries;
|
|
||||||
private string _requestIdHeaderKey;
|
|
||||||
private bool _isCached;
|
|
||||||
private CacheOptions _fileCacheOptions;
|
|
||||||
private string _downstreamScheme;
|
|
||||||
private string _loadBalancer;
|
|
||||||
private bool _useQos;
|
|
||||||
private QoSOptions _qosOptions;
|
|
||||||
private HttpHandlerOptions _httpHandlerOptions;
|
|
||||||
private bool _enableRateLimiting;
|
|
||||||
private RateLimitOptions _rateLimitOptions;
|
|
||||||
private bool _useServiceDiscovery;
|
|
||||||
private string _serviceName;
|
|
||||||
private List<HeaderFindAndReplace> _upstreamHeaderFindAndReplace;
|
|
||||||
private List<HeaderFindAndReplace> _downstreamHeaderFindAndReplace;
|
|
||||||
private readonly List<DownstreamHostAndPort> _downstreamAddresses;
|
|
||||||
private string _upstreamHost;
|
private string _upstreamHost;
|
||||||
|
private List<DownstreamReRoute> _downstreamReRoutes;
|
||||||
|
|
||||||
public ReRouteBuilder()
|
public ReRouteBuilder()
|
||||||
{
|
{
|
||||||
_downstreamAddresses = new List<DownstreamHostAndPort>();
|
_downstreamReRoutes = new List<DownstreamReRoute>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReRouteBuilder WithDownstreamAddresses(List<DownstreamHostAndPort> downstreamAddresses)
|
public ReRouteBuilder WithDownstreamReRoute(DownstreamReRoute value)
|
||||||
{
|
{
|
||||||
_downstreamAddresses.AddRange(downstreamAddresses);
|
_downstreamReRoutes.Add(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReRouteBuilder WithDownstreamReRoutes(List<DownstreamReRoute> value)
|
||||||
|
{
|
||||||
|
_downstreamReRoutes = value;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,24 +38,6 @@ namespace Ocelot.Configuration.Builder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReRouteBuilder WithLoadBalancer(string loadBalancer)
|
|
||||||
{
|
|
||||||
_loadBalancer = loadBalancer;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReRouteBuilder WithDownstreamScheme(string downstreamScheme)
|
|
||||||
{
|
|
||||||
_downstreamScheme = downstreamScheme;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReRouteBuilder WithDownstreamPathTemplate(string input)
|
|
||||||
{
|
|
||||||
_downstreamPathTemplate = input;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReRouteBuilder WithUpstreamPathTemplate(string input)
|
public ReRouteBuilder WithUpstreamPathTemplate(string input)
|
||||||
{
|
{
|
||||||
_upstreamTemplate = input;
|
_upstreamTemplate = input;
|
||||||
@ -91,158 +56,15 @@ namespace Ocelot.Configuration.Builder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReRouteBuilder WithIsAuthenticated(bool input)
|
|
||||||
{
|
|
||||||
_isAuthenticated = input;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReRouteBuilder WithIsAuthorised(bool input)
|
|
||||||
{
|
|
||||||
_isAuthorised = input;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReRouteBuilder WithRequestIdKey(string input)
|
|
||||||
{
|
|
||||||
_requestIdHeaderKey = input;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReRouteBuilder WithClaimsToHeaders(List<ClaimToThing> input)
|
|
||||||
{
|
|
||||||
_configHeaderExtractorProperties = input;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReRouteBuilder WithClaimsToClaims(List<ClaimToThing> input)
|
|
||||||
{
|
|
||||||
_claimToClaims = input;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReRouteBuilder WithRouteClaimsRequirement(Dictionary<string, string> input)
|
|
||||||
{
|
|
||||||
_routeClaimRequirement = input;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReRouteBuilder WithClaimsToQueries(List<ClaimToThing> input)
|
|
||||||
{
|
|
||||||
_claimToQueries = input;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReRouteBuilder WithIsCached(bool input)
|
|
||||||
{
|
|
||||||
_isCached = input;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReRouteBuilder WithCacheOptions(CacheOptions input)
|
|
||||||
{
|
|
||||||
_fileCacheOptions = input;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReRouteBuilder WithIsQos(bool input)
|
|
||||||
{
|
|
||||||
_useQos = input;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReRouteBuilder WithQosOptions(QoSOptions input)
|
|
||||||
{
|
|
||||||
_qosOptions = input;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReRouteBuilder WithReRouteKey(string reRouteKey)
|
|
||||||
{
|
|
||||||
_reRouteKey = reRouteKey;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReRouteBuilder WithAuthenticationOptions(AuthenticationOptions authenticationOptions)
|
|
||||||
{
|
|
||||||
_authenticationOptions = authenticationOptions;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReRouteBuilder WithEnableRateLimiting(bool input)
|
|
||||||
{
|
|
||||||
_enableRateLimiting = input;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReRouteBuilder WithRateLimitOptions(RateLimitOptions input)
|
|
||||||
{
|
|
||||||
_rateLimitOptions = input;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReRouteBuilder WithHttpHandlerOptions(HttpHandlerOptions input)
|
|
||||||
{
|
|
||||||
_httpHandlerOptions = input;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReRouteBuilder WithUseServiceDiscovery(bool useServiceDiscovery)
|
|
||||||
{
|
|
||||||
_useServiceDiscovery = useServiceDiscovery;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReRouteBuilder WithServiceName(string serviceName)
|
|
||||||
{
|
|
||||||
_serviceName = serviceName;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReRouteBuilder WithUpstreamHeaderFindAndReplace(List<HeaderFindAndReplace> upstreamHeaderFindAndReplace)
|
|
||||||
{
|
|
||||||
_upstreamHeaderFindAndReplace = upstreamHeaderFindAndReplace;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReRouteBuilder WithDownstreamHeaderFindAndReplace(List<HeaderFindAndReplace> downstreamHeaderFindAndReplace)
|
|
||||||
{
|
|
||||||
_downstreamHeaderFindAndReplace = downstreamHeaderFindAndReplace;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public ReRoute Build()
|
public ReRoute Build()
|
||||||
{
|
{
|
||||||
return new ReRoute(
|
return new ReRoute(
|
||||||
new PathTemplate(_downstreamPathTemplate),
|
_downstreamReRoutes,
|
||||||
new PathTemplate(_upstreamTemplate),
|
new PathTemplate(_upstreamTemplate),
|
||||||
_upstreamHttpMethod,
|
_upstreamHttpMethod,
|
||||||
_upstreamTemplatePattern,
|
_upstreamTemplatePattern,
|
||||||
_isAuthenticated,
|
_upstreamHost
|
||||||
_authenticationOptions,
|
);
|
||||||
_configHeaderExtractorProperties,
|
|
||||||
_claimToClaims,
|
|
||||||
_routeClaimRequirement,
|
|
||||||
_isAuthorised,
|
|
||||||
_claimToQueries,
|
|
||||||
_requestIdHeaderKey,
|
|
||||||
_isCached,
|
|
||||||
_fileCacheOptions,
|
|
||||||
_downstreamScheme,
|
|
||||||
_loadBalancer,
|
|
||||||
_reRouteKey,
|
|
||||||
_useQos,
|
|
||||||
_qosOptions,
|
|
||||||
_enableRateLimiting,
|
|
||||||
_rateLimitOptions,
|
|
||||||
_httpHandlerOptions,
|
|
||||||
_useServiceDiscovery,
|
|
||||||
_serviceName,
|
|
||||||
_upstreamHeaderFindAndReplace,
|
|
||||||
_downstreamHeaderFindAndReplace,
|
|
||||||
_downstreamAddresses,
|
|
||||||
_upstreamHost);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Ocelot.Cache;
|
using Ocelot.Cache;
|
||||||
using Ocelot.Configuration.Builder;
|
using Ocelot.Configuration.Builder;
|
||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
using Ocelot.Configuration.Parser;
|
|
||||||
using Ocelot.Configuration.Validator;
|
using Ocelot.Configuration.Validator;
|
||||||
using Ocelot.DependencyInjection;
|
using Ocelot.DependencyInjection;
|
||||||
using Ocelot.LoadBalancer;
|
|
||||||
using Ocelot.LoadBalancer.LoadBalancers;
|
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Requester.QoS;
|
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Creator
|
namespace Ocelot.Configuration.Creator
|
||||||
@ -97,7 +92,16 @@ namespace Ocelot.Configuration.Creator
|
|||||||
|
|
||||||
foreach (var reRoute in fileConfiguration.ReRoutes)
|
foreach (var reRoute in fileConfiguration.ReRoutes)
|
||||||
{
|
{
|
||||||
var ocelotReRoute = SetUpReRoute(reRoute, fileConfiguration.GlobalConfiguration);
|
var downstreamReRoute = SetUpDownstreamReRoute(reRoute, fileConfiguration.GlobalConfiguration);
|
||||||
|
|
||||||
|
var ocelotReRoute = SetUpReRoute(reRoute, downstreamReRoute);
|
||||||
|
|
||||||
|
reRoutes.Add(ocelotReRoute);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var aggregate in fileConfiguration.Aggregates)
|
||||||
|
{
|
||||||
|
var ocelotReRoute = SetUpAggregateReRoute(reRoutes, aggregate, fileConfiguration.GlobalConfiguration);
|
||||||
reRoutes.Add(ocelotReRoute);
|
reRoutes.Add(ocelotReRoute);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,7 +112,48 @@ namespace Ocelot.Configuration.Creator
|
|||||||
return new OkResponse<IOcelotConfiguration>(config);
|
return new OkResponse<IOcelotConfiguration>(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ReRoute SetUpReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration)
|
public ReRoute SetUpAggregateReRoute(List<ReRoute> reRoutes, FileAggregateReRoute aggregateReRoute, FileGlobalConfiguration globalConfiguration)
|
||||||
|
{
|
||||||
|
var applicableReRoutes = reRoutes
|
||||||
|
.SelectMany(x => x.DownstreamReRoute)
|
||||||
|
.Where(r => aggregateReRoute.ReRouteKeys.Contains(r.Key))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if(applicableReRoutes.Count != aggregateReRoute.ReRouteKeys.Count)
|
||||||
|
{
|
||||||
|
//todo - log or throw or return error whatever?
|
||||||
|
}
|
||||||
|
|
||||||
|
//make another re route out of these
|
||||||
|
var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(aggregateReRoute);
|
||||||
|
|
||||||
|
var reRoute = new ReRouteBuilder()
|
||||||
|
.WithUpstreamPathTemplate(aggregateReRoute.UpstreamPathTemplate)
|
||||||
|
.WithUpstreamHttpMethod(aggregateReRoute.UpstreamHttpMethod)
|
||||||
|
.WithUpstreamTemplatePattern(upstreamTemplatePattern)
|
||||||
|
.WithDownstreamReRoutes(applicableReRoutes)
|
||||||
|
.WithUpstreamHost(aggregateReRoute.UpstreamHost)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
return reRoute;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ReRoute SetUpReRoute(FileReRoute fileReRoute, DownstreamReRoute downstreamReRoutes)
|
||||||
|
{
|
||||||
|
var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileReRoute);
|
||||||
|
|
||||||
|
var reRoute = new ReRouteBuilder()
|
||||||
|
.WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate)
|
||||||
|
.WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod)
|
||||||
|
.WithUpstreamTemplatePattern(upstreamTemplatePattern)
|
||||||
|
.WithDownstreamReRoute(downstreamReRoutes)
|
||||||
|
.WithUpstreamHost(fileReRoute.UpstreamHost)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
return reRoute;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DownstreamReRoute SetUpDownstreamReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration)
|
||||||
{
|
{
|
||||||
var fileReRouteOptions = _fileReRouteOptionsCreator.Create(fileReRoute);
|
var fileReRouteOptions = _fileReRouteOptionsCreator.Create(fileReRoute);
|
||||||
|
|
||||||
@ -138,7 +183,8 @@ namespace Ocelot.Configuration.Creator
|
|||||||
|
|
||||||
var downstreamAddresses = _downstreamAddressesCreator.Create(fileReRoute);
|
var downstreamAddresses = _downstreamAddressesCreator.Create(fileReRoute);
|
||||||
|
|
||||||
var reRoute = new ReRouteBuilder()
|
var reRoute = new DownstreamReRouteBuilder()
|
||||||
|
.WithKey(fileReRoute.Key)
|
||||||
.WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate)
|
.WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate)
|
||||||
.WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate)
|
.WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate)
|
||||||
.WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod)
|
.WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod)
|
||||||
|
@ -5,6 +5,6 @@ namespace Ocelot.Configuration.Creator
|
|||||||
{
|
{
|
||||||
public interface IUpstreamTemplatePatternCreator
|
public interface IUpstreamTemplatePatternCreator
|
||||||
{
|
{
|
||||||
UpstreamPathTemplate Create(FileReRoute reRoute);
|
UpstreamPathTemplate Create(IReRoute reRoute);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -12,7 +12,7 @@ namespace Ocelot.Configuration.Creator
|
|||||||
private const string RegExForwardSlashOnly = "^/$";
|
private const string RegExForwardSlashOnly = "^/$";
|
||||||
private const string RegExForwardSlashAndOnePlaceHolder = "^/.*";
|
private const string RegExForwardSlashAndOnePlaceHolder = "^/.*";
|
||||||
|
|
||||||
public UpstreamPathTemplate Create(FileReRoute reRoute)
|
public UpstreamPathTemplate Create(IReRoute reRoute)
|
||||||
{
|
{
|
||||||
var upstreamTemplate = reRoute.UpstreamPathTemplate;
|
var upstreamTemplate = reRoute.UpstreamPathTemplate;
|
||||||
|
|
||||||
|
91
src/Ocelot/Configuration/DownstreamReRoute.cs
Normal file
91
src/Ocelot/Configuration/DownstreamReRoute.cs
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Ocelot.Values;
|
||||||
|
|
||||||
|
namespace Ocelot.Configuration
|
||||||
|
{
|
||||||
|
public class DownstreamReRoute
|
||||||
|
{
|
||||||
|
public DownstreamReRoute(
|
||||||
|
string key,
|
||||||
|
PathTemplate upstreamPathTemplate,
|
||||||
|
List<HeaderFindAndReplace> upstreamHeadersFindAndReplace,
|
||||||
|
List<HeaderFindAndReplace> downstreamHeadersFindAndReplace,
|
||||||
|
List<DownstreamHostAndPort> downstreamAddresses,
|
||||||
|
string serviceName,
|
||||||
|
HttpHandlerOptions httpHandlerOptions,
|
||||||
|
bool useServiceDiscovery,
|
||||||
|
bool enableEndpointEndpointRateLimiting,
|
||||||
|
bool isQos,
|
||||||
|
QoSOptions qosOptionsOptions,
|
||||||
|
string downstreamScheme,
|
||||||
|
string requestIdKey,
|
||||||
|
bool isCached,
|
||||||
|
CacheOptions cacheOptions,
|
||||||
|
string loadBalancer,
|
||||||
|
RateLimitOptions rateLimitOptions,
|
||||||
|
Dictionary<string, string> routeClaimsRequirement,
|
||||||
|
List<ClaimToThing> claimsToQueries,
|
||||||
|
List<ClaimToThing> claimsToHeaders,
|
||||||
|
List<ClaimToThing> claimsToClaims,
|
||||||
|
bool isAuthenticated,
|
||||||
|
bool isAuthorised,
|
||||||
|
AuthenticationOptions authenticationOptions,
|
||||||
|
PathTemplate downstreamPathTemplate,
|
||||||
|
string reRouteKey)
|
||||||
|
{
|
||||||
|
Key = key;
|
||||||
|
UpstreamPathTemplate = upstreamPathTemplate;
|
||||||
|
UpstreamHeadersFindAndReplace = upstreamHeadersFindAndReplace ?? new List<HeaderFindAndReplace>();
|
||||||
|
DownstreamHeadersFindAndReplace = downstreamHeadersFindAndReplace ?? new List<HeaderFindAndReplace>();
|
||||||
|
DownstreamAddresses = downstreamAddresses ?? new List<DownstreamHostAndPort>();
|
||||||
|
ServiceName = serviceName;
|
||||||
|
HttpHandlerOptions = httpHandlerOptions;
|
||||||
|
UseServiceDiscovery = useServiceDiscovery;
|
||||||
|
EnableEndpointEndpointRateLimiting = enableEndpointEndpointRateLimiting;
|
||||||
|
IsQos = isQos;
|
||||||
|
QosOptionsOptions = qosOptionsOptions;
|
||||||
|
DownstreamScheme = downstreamScheme;
|
||||||
|
RequestIdKey = requestIdKey;
|
||||||
|
IsCached = isCached;
|
||||||
|
CacheOptions = cacheOptions;
|
||||||
|
LoadBalancer = loadBalancer;
|
||||||
|
RateLimitOptions = rateLimitOptions;
|
||||||
|
RouteClaimsRequirement = routeClaimsRequirement;
|
||||||
|
ClaimsToQueries = claimsToQueries ?? new List<ClaimToThing>();
|
||||||
|
ClaimsToHeaders = claimsToHeaders ?? new List<ClaimToThing>();
|
||||||
|
ClaimsToClaims = claimsToClaims ?? new List<ClaimToThing>();
|
||||||
|
IsAuthenticated = isAuthenticated;
|
||||||
|
IsAuthorised = isAuthorised;
|
||||||
|
AuthenticationOptions = authenticationOptions;
|
||||||
|
DownstreamPathTemplate = downstreamPathTemplate;
|
||||||
|
ReRouteKey = reRouteKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Key { get; private set; }
|
||||||
|
public PathTemplate UpstreamPathTemplate { get;private set; }
|
||||||
|
public List<HeaderFindAndReplace> UpstreamHeadersFindAndReplace {get;private set;}
|
||||||
|
public List<HeaderFindAndReplace> DownstreamHeadersFindAndReplace { get; private set; }
|
||||||
|
public List<DownstreamHostAndPort> DownstreamAddresses { get; private set; }
|
||||||
|
public string ServiceName { get; private set; }
|
||||||
|
public HttpHandlerOptions HttpHandlerOptions { get; private set; }
|
||||||
|
public bool UseServiceDiscovery { get; private set; }
|
||||||
|
public bool EnableEndpointEndpointRateLimiting { get; private set; }
|
||||||
|
public bool IsQos { get; private set; }
|
||||||
|
public QoSOptions QosOptionsOptions { get; private set; }
|
||||||
|
public string DownstreamScheme { get; private set; }
|
||||||
|
public string RequestIdKey { get; private set; }
|
||||||
|
public bool IsCached { get; private set; }
|
||||||
|
public CacheOptions CacheOptions { get; private set; }
|
||||||
|
public string LoadBalancer { get; private set; }
|
||||||
|
public RateLimitOptions RateLimitOptions { get; private set; }
|
||||||
|
public Dictionary<string, string> RouteClaimsRequirement { get; private set; }
|
||||||
|
public List<ClaimToThing> ClaimsToQueries { get; private set; }
|
||||||
|
public List<ClaimToThing> ClaimsToHeaders { get; private set; }
|
||||||
|
public List<ClaimToThing> ClaimsToClaims { get; private set; }
|
||||||
|
public bool IsAuthenticated { get; private set; }
|
||||||
|
public bool IsAuthorised { get; private set; }
|
||||||
|
public AuthenticationOptions AuthenticationOptions { get; private set; }
|
||||||
|
public PathTemplate DownstreamPathTemplate { get; private set; }
|
||||||
|
public string ReRouteKey { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
18
src/Ocelot/Configuration/File/FileAggregateReRoute.cs
Normal file
18
src/Ocelot/Configuration/File/FileAggregateReRoute.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ocelot.Configuration.File
|
||||||
|
{
|
||||||
|
public class FileAggregateReRoute : IReRoute
|
||||||
|
{
|
||||||
|
public List<string> ReRouteKeys { get;set; }
|
||||||
|
public string UpstreamPathTemplate { get;set; }
|
||||||
|
public string UpstreamHost { get; set; }
|
||||||
|
public bool ReRouteIsCaseSensitive { get; set; }
|
||||||
|
|
||||||
|
// Only supports GET..are you crazy!! POST, PUT WOULD BE CRAZY!! :)
|
||||||
|
public List<string> UpstreamHttpMethod
|
||||||
|
{
|
||||||
|
get { return new List<string> {"Get"}; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,9 +8,12 @@ namespace Ocelot.Configuration.File
|
|||||||
{
|
{
|
||||||
ReRoutes = new List<FileReRoute>();
|
ReRoutes = new List<FileReRoute>();
|
||||||
GlobalConfiguration = new FileGlobalConfiguration();
|
GlobalConfiguration = new FileGlobalConfiguration();
|
||||||
|
Aggregates = new List<FileAggregateReRoute>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FileReRoute> ReRoutes { get; set; }
|
public List<FileReRoute> ReRoutes { get; set; }
|
||||||
|
// Seperate field for aggregates because this let's you re-use ReRoutes in multiple Aggregates
|
||||||
|
public List<FileAggregateReRoute> Aggregates { get;set; }
|
||||||
public FileGlobalConfiguration GlobalConfiguration { get; set; }
|
public FileGlobalConfiguration GlobalConfiguration { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Ocelot.Configuration.File
|
namespace Ocelot.Configuration.File
|
||||||
{
|
{
|
||||||
public class FileReRoute
|
public class FileReRoute : IReRoute
|
||||||
{
|
{
|
||||||
public FileReRoute()
|
public FileReRoute()
|
||||||
{
|
{
|
||||||
@ -36,12 +36,13 @@ namespace Ocelot.Configuration.File
|
|||||||
public string ServiceName { get; set; }
|
public string ServiceName { get; set; }
|
||||||
public string DownstreamScheme {get;set;}
|
public string DownstreamScheme {get;set;}
|
||||||
public FileQoSOptions QoSOptions { get; set; }
|
public FileQoSOptions QoSOptions { get; set; }
|
||||||
public string LoadBalancer {get;set;}
|
public string LoadBalancer { get;set; }
|
||||||
public FileRateLimitRule RateLimitOptions { get; set; }
|
public FileRateLimitRule RateLimitOptions { get; set; }
|
||||||
public FileAuthenticationOptions AuthenticationOptions { get; set; }
|
public FileAuthenticationOptions AuthenticationOptions { get; set; }
|
||||||
public FileHttpHandlerOptions HttpHandlerOptions { get; set; }
|
public FileHttpHandlerOptions HttpHandlerOptions { get; set; }
|
||||||
public bool UseServiceDiscovery {get;set;}
|
public bool UseServiceDiscovery { get;set; }
|
||||||
public List<FileHostAndPort> DownstreamHostAndPorts {get;set;}
|
public List<FileHostAndPort> DownstreamHostAndPorts {get;set;}
|
||||||
public string UpstreamHost { get; set; }
|
public string UpstreamHost { get; set; }
|
||||||
|
public string Key { get;set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
8
src/Ocelot/Configuration/File/IReRoute.cs
Normal file
8
src/Ocelot/Configuration/File/IReRoute.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace Ocelot.Configuration.File
|
||||||
|
{
|
||||||
|
public interface IReRoute
|
||||||
|
{
|
||||||
|
string UpstreamPathTemplate { get; set; }
|
||||||
|
bool ReRouteIsCaseSensitive { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -1,98 +1,30 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using Ocelot.Configuration.Creator;
|
using Ocelot.Configuration.Creator;
|
||||||
|
using Ocelot.Requester.QoS;
|
||||||
using Ocelot.Values;
|
using Ocelot.Values;
|
||||||
|
|
||||||
namespace Ocelot.Configuration
|
namespace Ocelot.Configuration
|
||||||
{
|
{
|
||||||
public class ReRoute
|
public class ReRoute
|
||||||
{
|
{
|
||||||
public ReRoute(PathTemplate downstreamPathTemplate,
|
public ReRoute(List<DownstreamReRoute> downstreamReRoute,
|
||||||
PathTemplate upstreamPathTemplate,
|
PathTemplate upstreamPathTemplate,
|
||||||
List<HttpMethod> upstreamHttpMethod,
|
List<HttpMethod> upstreamHttpMethod,
|
||||||
UpstreamPathTemplate upstreamTemplatePattern,
|
UpstreamPathTemplate upstreamTemplatePattern,
|
||||||
bool isAuthenticated,
|
|
||||||
AuthenticationOptions authenticationOptions,
|
|
||||||
List<ClaimToThing> claimsToHeaders,
|
|
||||||
List<ClaimToThing> claimsToClaims,
|
|
||||||
Dictionary<string, string> routeClaimsRequirement,
|
|
||||||
bool isAuthorised,
|
|
||||||
List<ClaimToThing> claimsToQueries,
|
|
||||||
string requestIdKey,
|
|
||||||
bool isCached,
|
|
||||||
CacheOptions cacheOptions,
|
|
||||||
string downstreamScheme,
|
|
||||||
string loadBalancer,
|
|
||||||
string reRouteKey,
|
|
||||||
bool isQos,
|
|
||||||
QoSOptions qosOptions,
|
|
||||||
bool enableEndpointRateLimiting,
|
|
||||||
RateLimitOptions ratelimitOptions,
|
|
||||||
HttpHandlerOptions httpHandlerOptions,
|
|
||||||
bool useServiceDiscovery,
|
|
||||||
string serviceName,
|
|
||||||
List<HeaderFindAndReplace> upstreamHeadersFindAndReplace,
|
|
||||||
List<HeaderFindAndReplace> downstreamHeadersFindAndReplace,
|
|
||||||
List<DownstreamHostAndPort> downstreamAddresses,
|
|
||||||
string upstreamHost)
|
string upstreamHost)
|
||||||
{
|
{
|
||||||
UpstreamHost = upstreamHost;
|
UpstreamHost = upstreamHost;
|
||||||
DownstreamHeadersFindAndReplace = downstreamHeadersFindAndReplace ?? new List<HeaderFindAndReplace>();
|
DownstreamReRoute = downstreamReRoute;
|
||||||
UpstreamHeadersFindAndReplace = upstreamHeadersFindAndReplace ?? new List<HeaderFindAndReplace>();
|
|
||||||
ServiceName = serviceName;
|
|
||||||
UseServiceDiscovery = useServiceDiscovery;
|
|
||||||
ReRouteKey = reRouteKey;
|
|
||||||
LoadBalancer = loadBalancer;
|
|
||||||
DownstreamAddresses = downstreamAddresses ?? new List<DownstreamHostAndPort>();
|
|
||||||
DownstreamPathTemplate = downstreamPathTemplate;
|
|
||||||
UpstreamPathTemplate = upstreamPathTemplate;
|
UpstreamPathTemplate = upstreamPathTemplate;
|
||||||
UpstreamHttpMethod = upstreamHttpMethod;
|
UpstreamHttpMethod = upstreamHttpMethod;
|
||||||
UpstreamTemplatePattern = upstreamTemplatePattern;
|
UpstreamTemplatePattern = upstreamTemplatePattern;
|
||||||
IsAuthenticated = isAuthenticated;
|
|
||||||
AuthenticationOptions = authenticationOptions;
|
|
||||||
RouteClaimsRequirement = routeClaimsRequirement;
|
|
||||||
IsAuthorised = isAuthorised;
|
|
||||||
RequestIdKey = requestIdKey;
|
|
||||||
IsCached = isCached;
|
|
||||||
CacheOptions = cacheOptions;
|
|
||||||
ClaimsToQueries = claimsToQueries ?? new List<ClaimToThing>();
|
|
||||||
ClaimsToClaims = claimsToClaims ?? new List<ClaimToThing>();
|
|
||||||
ClaimsToHeaders = claimsToHeaders ?? new List<ClaimToThing>();
|
|
||||||
DownstreamScheme = downstreamScheme;
|
|
||||||
IsQos = isQos;
|
|
||||||
QosOptionsOptions = qosOptions;
|
|
||||||
EnableEndpointEndpointRateLimiting = enableEndpointRateLimiting;
|
|
||||||
RateLimitOptions = ratelimitOptions;
|
|
||||||
HttpHandlerOptions = httpHandlerOptions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ReRouteKey {get;private set;}
|
|
||||||
public PathTemplate DownstreamPathTemplate { get; private set; }
|
|
||||||
public PathTemplate UpstreamPathTemplate { get; private set; }
|
public PathTemplate UpstreamPathTemplate { get; private set; }
|
||||||
public UpstreamPathTemplate UpstreamTemplatePattern { get; private set; }
|
public UpstreamPathTemplate UpstreamTemplatePattern { get; private set; }
|
||||||
public List<HttpMethod> UpstreamHttpMethod { get; private set; }
|
public List<HttpMethod> UpstreamHttpMethod { get; private set; }
|
||||||
public bool IsAuthenticated { get; private set; }
|
|
||||||
public bool IsAuthorised { get; private set; }
|
|
||||||
public AuthenticationOptions AuthenticationOptions { get; private set; }
|
|
||||||
public List<ClaimToThing> ClaimsToQueries { get; private set; }
|
|
||||||
public List<ClaimToThing> ClaimsToHeaders { get; private set; }
|
|
||||||
public List<ClaimToThing> ClaimsToClaims { get; private set; }
|
|
||||||
public Dictionary<string, string> RouteClaimsRequirement { get; private set; }
|
|
||||||
public string RequestIdKey { get; private set; }
|
|
||||||
public bool IsCached { get; private set; }
|
|
||||||
public CacheOptions CacheOptions { get; private set; }
|
|
||||||
public string DownstreamScheme {get;private set;}
|
|
||||||
public bool IsQos { get; private set; }
|
|
||||||
public QoSOptions QosOptionsOptions { get; private set; }
|
|
||||||
public string LoadBalancer {get;private set;}
|
|
||||||
public bool EnableEndpointEndpointRateLimiting { get; private set; }
|
|
||||||
public RateLimitOptions RateLimitOptions { get; private set; }
|
|
||||||
public HttpHandlerOptions HttpHandlerOptions { get; private set; }
|
|
||||||
public bool UseServiceDiscovery {get;private set;}
|
|
||||||
public string ServiceName {get;private set;}
|
|
||||||
public List<HeaderFindAndReplace> UpstreamHeadersFindAndReplace {get;private set;}
|
|
||||||
public List<HeaderFindAndReplace> DownstreamHeadersFindAndReplace {get;private set;}
|
|
||||||
public List<DownstreamHostAndPort> DownstreamAddresses {get;private set;}
|
|
||||||
public string UpstreamHost { get; private set; }
|
public string UpstreamHost { get; private set; }
|
||||||
|
public List<DownstreamReRoute> DownstreamReRoute { get; private set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,11 +11,11 @@ namespace Ocelot.Configuration.Repository
|
|||||||
{
|
{
|
||||||
public class ConsulFileConfigurationPoller : IDisposable
|
public class ConsulFileConfigurationPoller : IDisposable
|
||||||
{
|
{
|
||||||
private IOcelotLogger _logger;
|
private readonly IOcelotLogger _logger;
|
||||||
private IFileConfigurationRepository _repo;
|
private readonly IFileConfigurationRepository _repo;
|
||||||
private IFileConfigurationSetter _setter;
|
private readonly IFileConfigurationSetter _setter;
|
||||||
private string _previousAsJson;
|
private string _previousAsJson;
|
||||||
private Timer _timer;
|
private readonly Timer _timer;
|
||||||
private bool _polling;
|
private bool _polling;
|
||||||
|
|
||||||
public ConsulFileConfigurationPoller(IOcelotLoggerFactory factory, IFileConfigurationRepository repo, IFileConfigurationSetter setter)
|
public ConsulFileConfigurationPoller(IOcelotLoggerFactory factory, IFileConfigurationRepository repo, IFileConfigurationSetter setter)
|
||||||
|
@ -19,6 +19,29 @@ namespace Ocelot.Configuration.Validator
|
|||||||
RuleForEach(configuration => configuration.ReRoutes)
|
RuleForEach(configuration => configuration.ReRoutes)
|
||||||
.Must((config, reRoute) => IsNotDuplicateIn(reRoute, config.ReRoutes))
|
.Must((config, reRoute) => IsNotDuplicateIn(reRoute, config.ReRoutes))
|
||||||
.WithMessage((config, reRoute) => $"{nameof(reRoute)} {reRoute.UpstreamPathTemplate} has duplicate");
|
.WithMessage((config, reRoute) => $"{nameof(reRoute)} {reRoute.UpstreamPathTemplate} has duplicate");
|
||||||
|
|
||||||
|
RuleForEach(configuration => configuration.ReRoutes)
|
||||||
|
.Must((config, reRoute) => IsNotDuplicateIn(reRoute, config.Aggregates))
|
||||||
|
.WithMessage((config, reRoute) => $"{nameof(reRoute)} {reRoute.UpstreamPathTemplate} has duplicate aggregate");
|
||||||
|
|
||||||
|
RuleForEach(configuration => configuration.Aggregates)
|
||||||
|
.Must((config, aggregateReRoute) => IsNotDuplicateIn(aggregateReRoute, config.Aggregates))
|
||||||
|
.WithMessage((config, aggregate) => $"{nameof(aggregate)} {aggregate.UpstreamPathTemplate} has duplicate aggregate");
|
||||||
|
|
||||||
|
RuleForEach(configuration => configuration.Aggregates)
|
||||||
|
.Must((config, aggregateReRoute) => AllReRoutesForAggregateExist(aggregateReRoute, config.ReRoutes))
|
||||||
|
.WithMessage((config, aggregateReRoute) => $"ReRoutes for {nameof(aggregateReRoute)} {aggregateReRoute.UpstreamPathTemplate} either do not exist or do not have correct Key property");
|
||||||
|
|
||||||
|
RuleForEach(configuration => configuration.Aggregates)
|
||||||
|
.Must((config, aggregateReRoute) => DoesNotContainReRoutesWithSpecificRequestIdKeys(aggregateReRoute, config.ReRoutes))
|
||||||
|
.WithMessage((config, aggregateReRoute) => $"{nameof(aggregateReRoute)} {aggregateReRoute.UpstreamPathTemplate} contains ReRoute with specific RequestIdKey, this is not possible with Aggregates");
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool AllReRoutesForAggregateExist(FileAggregateReRoute fileAggregateReRoute, List<FileReRoute> reRoutes)
|
||||||
|
{
|
||||||
|
var reRoutesForAggregate = reRoutes.Where(r => fileAggregateReRoute.ReRouteKeys.Contains(r.Key));
|
||||||
|
|
||||||
|
return reRoutesForAggregate.Count() == fileAggregateReRoute.ReRouteKeys.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Response<ConfigurationValidationResult>> IsValid(FileConfiguration configuration)
|
public async Task<Response<ConfigurationValidationResult>> IsValid(FileConfiguration configuration)
|
||||||
@ -37,10 +60,21 @@ namespace Ocelot.Configuration.Validator
|
|||||||
return new OkResponse<ConfigurationValidationResult>(result);
|
return new OkResponse<ConfigurationValidationResult>(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsNotDuplicateIn(FileReRoute reRoute, List<FileReRoute> reRoutes)
|
private static bool DoesNotContainReRoutesWithSpecificRequestIdKeys(FileAggregateReRoute fileAggregateReRoute,
|
||||||
|
List<FileReRoute> reRoutes)
|
||||||
|
{
|
||||||
|
var reRoutesForAggregate = reRoutes.Where(r => fileAggregateReRoute.ReRouteKeys.Contains(r.Key));
|
||||||
|
|
||||||
|
return reRoutesForAggregate.All(r => string.IsNullOrEmpty(r.RequestIdKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsNotDuplicateIn(FileReRoute reRoute,
|
||||||
|
List<FileReRoute> reRoutes)
|
||||||
{
|
{
|
||||||
var matchingReRoutes = reRoutes
|
var matchingReRoutes = reRoutes
|
||||||
.Where(r => r.UpstreamPathTemplate == reRoute.UpstreamPathTemplate && (r.UpstreamHost != reRoute.UpstreamHost || reRoute.UpstreamHost == null)).ToList();
|
.Where(r => r.UpstreamPathTemplate == reRoute.UpstreamPathTemplate
|
||||||
|
&& (r.UpstreamHost != reRoute.UpstreamHost || reRoute.UpstreamHost == null))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
if(matchingReRoutes.Count == 1)
|
if(matchingReRoutes.Count == 1)
|
||||||
{
|
{
|
||||||
@ -62,5 +96,27 @@ namespace Ocelot.Configuration.Validator
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool IsNotDuplicateIn(FileReRoute reRoute,
|
||||||
|
List<FileAggregateReRoute> aggregateReRoutes)
|
||||||
|
{
|
||||||
|
var duplicate = aggregateReRoutes
|
||||||
|
.Any(a => a.UpstreamPathTemplate == reRoute.UpstreamPathTemplate
|
||||||
|
&& a.UpstreamHost == reRoute.UpstreamHost
|
||||||
|
&& reRoute.UpstreamHttpMethod.Select(x => x.ToLower()).Contains("get"));
|
||||||
|
|
||||||
|
return !duplicate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsNotDuplicateIn(FileAggregateReRoute reRoute,
|
||||||
|
List<FileAggregateReRoute> aggregateReRoutes)
|
||||||
|
{
|
||||||
|
var matchingReRoutes = aggregateReRoutes
|
||||||
|
.Where(r => r.UpstreamPathTemplate == reRoute.UpstreamPathTemplate
|
||||||
|
&& r.UpstreamHost == reRoute.UpstreamHost)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return matchingReRoutes.Count <= 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
using Butterfly.Client.Tracing;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using Ocelot.Middleware.Multiplexer;
|
||||||
|
|
||||||
namespace Ocelot.DependencyInjection
|
namespace Ocelot.DependencyInjection
|
||||||
{
|
{
|
||||||
@ -30,7 +32,6 @@ namespace Ocelot.DependencyInjection
|
|||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
using Ocelot.QueryStrings;
|
using Ocelot.QueryStrings;
|
||||||
using Ocelot.RateLimit;
|
using Ocelot.RateLimit;
|
||||||
using Ocelot.Request.Builder;
|
|
||||||
using Ocelot.Request.Mapper;
|
using Ocelot.Request.Mapper;
|
||||||
using Ocelot.Requester;
|
using Ocelot.Requester;
|
||||||
using Ocelot.Requester.QoS;
|
using Ocelot.Requester.QoS;
|
||||||
@ -114,7 +115,6 @@ namespace Ocelot.DependencyInjection
|
|||||||
_services.TryAddSingleton<IDownstreamRouteFinder, DownstreamRouteFinder>();
|
_services.TryAddSingleton<IDownstreamRouteFinder, DownstreamRouteFinder>();
|
||||||
_services.TryAddSingleton<IHttpRequester, HttpClientHttpRequester>();
|
_services.TryAddSingleton<IHttpRequester, HttpClientHttpRequester>();
|
||||||
_services.TryAddSingleton<IHttpResponder, HttpContextResponder>();
|
_services.TryAddSingleton<IHttpResponder, HttpContextResponder>();
|
||||||
_services.TryAddSingleton<IRequestCreator, HttpRequestCreator>();
|
|
||||||
_services.TryAddSingleton<IErrorsToHttpStatusCodeMapper, ErrorsToHttpStatusCodeMapper>();
|
_services.TryAddSingleton<IErrorsToHttpStatusCodeMapper, ErrorsToHttpStatusCodeMapper>();
|
||||||
_services.TryAddSingleton<IRateLimitCounterHandler, MemoryCacheRateLimitCounterHandler>();
|
_services.TryAddSingleton<IRateLimitCounterHandler, MemoryCacheRateLimitCounterHandler>();
|
||||||
_services.TryAddSingleton<IHttpClientCache, MemoryHttpClientCache>();
|
_services.TryAddSingleton<IHttpClientCache, MemoryHttpClientCache>();
|
||||||
@ -148,7 +148,13 @@ namespace Ocelot.DependencyInjection
|
|||||||
//these get picked out later and added to http request
|
//these get picked out later and added to http request
|
||||||
_provider = new DelegatingHandlerHandlerProvider();
|
_provider = new DelegatingHandlerHandlerProvider();
|
||||||
_services.TryAddSingleton<IDelegatingHandlerHandlerProvider>(_provider);
|
_services.TryAddSingleton<IDelegatingHandlerHandlerProvider>(_provider);
|
||||||
_services.AddTransient<ITracingHandler, NoTracingHandler>();
|
_services.TryAddSingleton<IMultiplexer, Multiplexer>();
|
||||||
|
_services.TryAddSingleton<IResponseAggregator, SimpleJsonResponseAggregator>();
|
||||||
|
_services.AddSingleton<ITracingHandlerFactory, TracingHandlerFactory>();
|
||||||
|
|
||||||
|
// We add this here so that we can always inject something into the factory for IoC..
|
||||||
|
_services.AddSingleton<IServiceTracer, FakeServiceTracer>();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IOcelotAdministrationBuilder AddAdministration(string path, string secret)
|
public IOcelotAdministrationBuilder AddAdministration(string path, string secret)
|
||||||
@ -191,7 +197,8 @@ namespace Ocelot.DependencyInjection
|
|||||||
|
|
||||||
public IOcelotBuilder AddOpenTracing(Action<ButterflyOptions> settings)
|
public IOcelotBuilder AddOpenTracing(Action<ButterflyOptions> settings)
|
||||||
{
|
{
|
||||||
_services.AddTransient<ITracingHandler, OcelotHttpTracingHandler>();
|
// Earlier we add FakeServiceTracer and need to remove it here before we add butterfly
|
||||||
|
_services.RemoveAll<IServiceTracer>();
|
||||||
_services.AddButterfly(settings);
|
_services.AddButterfly(settings);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,5 @@ namespace Ocelot.DownstreamRouteFinder
|
|||||||
}
|
}
|
||||||
public List<PlaceholderNameAndValue> TemplatePlaceholderNameAndValues { get; private set; }
|
public List<PlaceholderNameAndValue> TemplatePlaceholderNameAndValues { get; private set; }
|
||||||
public ReRoute ReRoute { get; private set; }
|
public ReRoute ReRoute { get; private set; }
|
||||||
public object UpstreamHeadersFindAndReplace {get;private set;}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,71 +1,71 @@
|
|||||||
using System.Linq;
|
|
||||||
using System.Security.Cryptography.X509Certificates;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Ocelot.Configuration;
|
||||||
using Ocelot.Configuration.Provider;
|
using Ocelot.Configuration.Provider;
|
||||||
using Ocelot.DownstreamRouteFinder.Finder;
|
using Ocelot.DownstreamRouteFinder.Finder;
|
||||||
using Ocelot.Infrastructure.Extensions;
|
using Ocelot.Infrastructure.Extensions;
|
||||||
using Ocelot.Infrastructure.RequestData;
|
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
|
using Ocelot.Middleware.Multiplexer;
|
||||||
|
|
||||||
namespace Ocelot.DownstreamRouteFinder.Middleware
|
namespace Ocelot.DownstreamRouteFinder.Middleware
|
||||||
{
|
{
|
||||||
public class DownstreamRouteFinderMiddleware : OcelotMiddleware
|
public class DownstreamRouteFinderMiddleware : OcelotMiddleware
|
||||||
{
|
{
|
||||||
private readonly RequestDelegate _next;
|
private readonly OcelotRequestDelegate _next;
|
||||||
private readonly IDownstreamRouteFinder _downstreamRouteFinder;
|
private readonly IDownstreamRouteFinder _downstreamRouteFinder;
|
||||||
private readonly IOcelotLogger _logger;
|
private readonly IOcelotLogger _logger;
|
||||||
private readonly IOcelotConfigurationProvider _configProvider;
|
private readonly IOcelotConfigurationProvider _configProvider;
|
||||||
|
private readonly IMultiplexer _multiplexer;
|
||||||
|
|
||||||
|
|
||||||
public DownstreamRouteFinderMiddleware(RequestDelegate next,
|
public DownstreamRouteFinderMiddleware(OcelotRequestDelegate next,
|
||||||
IOcelotLoggerFactory loggerFactory,
|
IOcelotLoggerFactory loggerFactory,
|
||||||
IDownstreamRouteFinder downstreamRouteFinder,
|
IDownstreamRouteFinder downstreamRouteFinder,
|
||||||
IRequestScopedDataRepository requestScopedDataRepository,
|
IOcelotConfigurationProvider configProvider,
|
||||||
IOcelotConfigurationProvider configProvider)
|
IMultiplexer multiplexer)
|
||||||
:base(requestScopedDataRepository)
|
|
||||||
{
|
{
|
||||||
_configProvider = configProvider;
|
_configProvider = configProvider;
|
||||||
|
_multiplexer = multiplexer;
|
||||||
_next = next;
|
_next = next;
|
||||||
_downstreamRouteFinder = downstreamRouteFinder;
|
_downstreamRouteFinder = downstreamRouteFinder;
|
||||||
_logger = loggerFactory.CreateLogger<DownstreamRouteFinderMiddleware>();
|
_logger = loggerFactory.CreateLogger<DownstreamRouteFinderMiddleware>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Invoke(HttpContext context)
|
public async Task Invoke(DownstreamContext context)
|
||||||
{
|
{
|
||||||
var upstreamUrlPath = context.Request.Path.ToString();
|
var upstreamUrlPath = context.HttpContext.Request.Path.ToString();
|
||||||
|
|
||||||
var upstreamHost = context.Request.Headers["Host"];
|
var upstreamHost = context.HttpContext.Request.Headers["Host"];
|
||||||
|
|
||||||
var configuration = await _configProvider.Get();
|
var configuration = await _configProvider.Get();
|
||||||
|
|
||||||
if(configuration.IsError)
|
if (configuration.IsError)
|
||||||
{
|
{
|
||||||
_logger.LogError($"{MiddlewareName} setting pipeline errors. IOcelotConfigurationProvider returned {configuration.Errors.ToErrorString()}");
|
_logger.LogError($"{MiddlewareName} setting pipeline errors. IOcelotConfigurationProvider returned {configuration.Errors.ToErrorString()}");
|
||||||
SetPipelineError(configuration.Errors);
|
SetPipelineError(context, configuration.Errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetServiceProviderConfigurationForThisRequest(configuration.Data.ServiceProviderConfiguration);
|
context.ServiceProviderConfiguration = configuration.Data.ServiceProviderConfiguration;
|
||||||
|
|
||||||
_logger.LogDebug("upstream url path is {upstreamUrlPath}", upstreamUrlPath);
|
_logger.LogDebug("upstream url path is {upstreamUrlPath}", upstreamUrlPath);
|
||||||
|
|
||||||
var downstreamRoute = _downstreamRouteFinder.FindDownstreamRoute(upstreamUrlPath, context.Request.Method, configuration.Data, upstreamHost);
|
var downstreamRoute = _downstreamRouteFinder.FindDownstreamRoute(upstreamUrlPath, context.HttpContext.Request.Method, configuration.Data, upstreamHost);
|
||||||
|
|
||||||
if (downstreamRoute.IsError)
|
if (downstreamRoute.IsError)
|
||||||
{
|
{
|
||||||
_logger.LogError($"{MiddlewareName} setting pipeline errors. IDownstreamRouteFinder returned {downstreamRoute.Errors.ToErrorString()}");
|
_logger.LogError($"{MiddlewareName} setting pipeline errors. IDownstreamRouteFinder returned {downstreamRoute.Errors.ToErrorString()}");
|
||||||
|
|
||||||
SetPipelineError(downstreamRoute.Errors);
|
SetPipelineError(context, downstreamRoute.Errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogDebug("downstream template is {downstreamRoute.Data.ReRoute.DownstreamPath}", downstreamRoute.Data.ReRoute.DownstreamPathTemplate);
|
//todo - put this back in
|
||||||
|
// _logger.LogDebug("downstream template is {downstreamRoute.Data.ReRoute.DownstreamPath}", downstreamRoute.Data.ReRoute.DownstreamReRoute.DownstreamPathTemplate);
|
||||||
|
|
||||||
SetDownstreamRouteForThisRequest(downstreamRoute.Data);
|
context.TemplatePlaceholderNameAndValues = downstreamRoute.Data.TemplatePlaceholderNameAndValues;
|
||||||
|
|
||||||
await _next.Invoke(context);
|
await _multiplexer.Multiplex(context, downstreamRoute.Data.ReRoute, _next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Ocelot.Middleware.Pipeline;
|
||||||
|
|
||||||
namespace Ocelot.DownstreamRouteFinder.Middleware
|
namespace Ocelot.DownstreamRouteFinder.Middleware
|
||||||
{
|
{
|
||||||
public static class DownstreamRouteFinderMiddlewareExtensions
|
public static class DownstreamRouteFinderMiddlewareExtensions
|
||||||
{
|
{
|
||||||
public static IApplicationBuilder UseDownstreamRouteFinderMiddleware(this IApplicationBuilder builder)
|
public static IOcelotPipelineBuilder UseDownstreamRouteFinderMiddleware(this IOcelotPipelineBuilder builder)
|
||||||
{
|
{
|
||||||
return builder.UseMiddleware<DownstreamRouteFinderMiddleware>();
|
return builder.UseMiddleware<DownstreamRouteFinderMiddleware>();
|
||||||
}
|
}
|
||||||
|
@ -5,22 +5,21 @@ using Ocelot.Infrastructure.RequestData;
|
|||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
using System;
|
using System;
|
||||||
|
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||||
|
|
||||||
namespace Ocelot.DownstreamUrlCreator.Middleware
|
namespace Ocelot.DownstreamUrlCreator.Middleware
|
||||||
{
|
{
|
||||||
public class DownstreamUrlCreatorMiddleware : OcelotMiddleware
|
public class DownstreamUrlCreatorMiddleware : OcelotMiddleware
|
||||||
{
|
{
|
||||||
private readonly RequestDelegate _next;
|
private readonly OcelotRequestDelegate _next;
|
||||||
private readonly IDownstreamPathPlaceholderReplacer _replacer;
|
private readonly IDownstreamPathPlaceholderReplacer _replacer;
|
||||||
private readonly IOcelotLogger _logger;
|
private readonly IOcelotLogger _logger;
|
||||||
private readonly IUrlBuilder _urlBuilder;
|
private readonly IUrlBuilder _urlBuilder;
|
||||||
|
|
||||||
public DownstreamUrlCreatorMiddleware(RequestDelegate next,
|
public DownstreamUrlCreatorMiddleware(OcelotRequestDelegate next,
|
||||||
IOcelotLoggerFactory loggerFactory,
|
IOcelotLoggerFactory loggerFactory,
|
||||||
IDownstreamPathPlaceholderReplacer replacer,
|
IDownstreamPathPlaceholderReplacer replacer,
|
||||||
IRequestScopedDataRepository requestScopedDataRepository,
|
|
||||||
IUrlBuilder urlBuilder)
|
IUrlBuilder urlBuilder)
|
||||||
:base(requestScopedDataRepository)
|
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_replacer = replacer;
|
_replacer = replacer;
|
||||||
@ -28,28 +27,28 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
|
|||||||
_logger = loggerFactory.CreateLogger<DownstreamUrlCreatorMiddleware>();
|
_logger = loggerFactory.CreateLogger<DownstreamUrlCreatorMiddleware>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Invoke(HttpContext context)
|
public async Task Invoke(DownstreamContext context)
|
||||||
{
|
{
|
||||||
var dsPath = _replacer
|
var dsPath = _replacer
|
||||||
.Replace(DownstreamRoute.ReRoute.DownstreamPathTemplate, DownstreamRoute.TemplatePlaceholderNameAndValues);
|
.Replace(context.DownstreamReRoute.DownstreamPathTemplate, context.TemplatePlaceholderNameAndValues);
|
||||||
|
|
||||||
if (dsPath.IsError)
|
if (dsPath.IsError)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("IDownstreamPathPlaceholderReplacer returned an error, setting pipeline error");
|
_logger.LogDebug("IDownstreamPathPlaceholderReplacer returned an error, setting pipeline error");
|
||||||
|
|
||||||
SetPipelineError(dsPath.Errors);
|
SetPipelineError(context, dsPath.Errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var uriBuilder = new UriBuilder(DownstreamRequest.RequestUri)
|
var uriBuilder = new UriBuilder(context.DownstreamRequest.RequestUri)
|
||||||
{
|
{
|
||||||
Path = dsPath.Data.Value,
|
Path = dsPath.Data.Value,
|
||||||
Scheme = DownstreamRoute.ReRoute.DownstreamScheme
|
Scheme = context.DownstreamReRoute.DownstreamScheme
|
||||||
};
|
};
|
||||||
|
|
||||||
DownstreamRequest.RequestUri = uriBuilder.Uri;
|
context.DownstreamRequest.RequestUri = uriBuilder.Uri;
|
||||||
|
|
||||||
_logger.LogDebug("downstream url is {downstreamUrl.Data.Value}", DownstreamRequest.RequestUri);
|
_logger.LogDebug("downstream url is {downstreamUrl.Data.Value}", context.DownstreamRequest.RequestUri);
|
||||||
|
|
||||||
await _next.Invoke(context);
|
await _next.Invoke(context);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Ocelot.Middleware.Pipeline;
|
||||||
|
|
||||||
namespace Ocelot.DownstreamUrlCreator.Middleware
|
namespace Ocelot.DownstreamUrlCreator.Middleware
|
||||||
{
|
{
|
||||||
public static class DownstreamUrlCreatorMiddlewareExtensions
|
public static class DownstreamUrlCreatorMiddlewareExtensions
|
||||||
{
|
{
|
||||||
public static IApplicationBuilder UseDownstreamUrlCreatorMiddleware(this IApplicationBuilder builder)
|
public static IOcelotPipelineBuilder UseDownstreamUrlCreatorMiddleware(this IOcelotPipelineBuilder builder)
|
||||||
{
|
{
|
||||||
return builder.UseMiddleware<DownstreamUrlCreatorMiddleware>();
|
return builder.UseMiddleware<DownstreamUrlCreatorMiddleware>();
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
using Ocelot.Configuration.Provider;
|
using Ocelot.Configuration.Provider;
|
||||||
|
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||||
using Ocelot.Infrastructure.Extensions;
|
using Ocelot.Infrastructure.Extensions;
|
||||||
using Ocelot.Infrastructure.RequestData;
|
using Ocelot.Infrastructure.RequestData;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
@ -16,24 +17,23 @@ namespace Ocelot.Errors.Middleware
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ExceptionHandlerMiddleware : OcelotMiddleware
|
public class ExceptionHandlerMiddleware : OcelotMiddleware
|
||||||
{
|
{
|
||||||
private readonly RequestDelegate _next;
|
private readonly OcelotRequestDelegate _next;
|
||||||
private readonly IOcelotLogger _logger;
|
private readonly IOcelotLogger _logger;
|
||||||
private readonly IRequestScopedDataRepository _requestScopedDataRepository;
|
private readonly IOcelotConfigurationProvider _provider;
|
||||||
private readonly IOcelotConfigurationProvider _configProvider;
|
private readonly IRequestScopedDataRepository _repo;
|
||||||
|
|
||||||
public ExceptionHandlerMiddleware(RequestDelegate next,
|
public ExceptionHandlerMiddleware(OcelotRequestDelegate next,
|
||||||
IOcelotLoggerFactory loggerFactory,
|
IOcelotLoggerFactory loggerFactory,
|
||||||
IRequestScopedDataRepository requestScopedDataRepository,
|
IOcelotConfigurationProvider provider,
|
||||||
IOcelotConfigurationProvider configProvider)
|
IRequestScopedDataRepository repo)
|
||||||
:base(requestScopedDataRepository)
|
|
||||||
{
|
{
|
||||||
_configProvider = configProvider;
|
_provider = provider;
|
||||||
|
_repo = repo;
|
||||||
_next = next;
|
_next = next;
|
||||||
_requestScopedDataRepository = requestScopedDataRepository;
|
|
||||||
_logger = loggerFactory.CreateLogger<ExceptionHandlerMiddleware>();
|
_logger = loggerFactory.CreateLogger<ExceptionHandlerMiddleware>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Invoke(HttpContext context)
|
public async Task Invoke(DownstreamContext context)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -57,12 +57,12 @@ namespace Ocelot.Errors.Middleware
|
|||||||
_logger.LogDebug("ocelot pipeline finished");
|
_logger.LogDebug("ocelot pipeline finished");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task TrySetGlobalRequestId(HttpContext context)
|
private async Task TrySetGlobalRequestId(DownstreamContext context)
|
||||||
{
|
{
|
||||||
//try and get the global request id and set it for logs...
|
//try and get the global request id and set it for logs...
|
||||||
//should 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
|
//first thing is get config
|
||||||
var configuration = await _configProvider.Get();
|
var configuration = await _provider.Get();
|
||||||
|
|
||||||
//if error throw to catch below..
|
//if error throw to catch below..
|
||||||
if(configuration.IsError)
|
if(configuration.IsError)
|
||||||
@ -74,22 +74,23 @@ namespace Ocelot.Errors.Middleware
|
|||||||
var key = configuration.Data.RequestId;
|
var key = configuration.Data.RequestId;
|
||||||
|
|
||||||
StringValues upstreamRequestIds;
|
StringValues upstreamRequestIds;
|
||||||
if (!string.IsNullOrEmpty(key) && context.Request.Headers.TryGetValue(key, out upstreamRequestIds))
|
if (!string.IsNullOrEmpty(key) && context.HttpContext.Request.Headers.TryGetValue(key, out upstreamRequestIds))
|
||||||
{
|
{
|
||||||
context.TraceIdentifier = upstreamRequestIds.First();
|
//todo fix looking in both places
|
||||||
_requestScopedDataRepository.Add<string>("RequestId", context.TraceIdentifier);
|
context.HttpContext.TraceIdentifier = upstreamRequestIds.First();
|
||||||
}
|
_repo.Add<string>("RequestId", context.HttpContext.TraceIdentifier);
|
||||||
}
|
|
||||||
|
|
||||||
private void SetInternalServerErrorOnResponse(HttpContext context)
|
|
||||||
{
|
|
||||||
if (!context.Response.HasStarted)
|
|
||||||
{
|
|
||||||
context.Response.StatusCode = 500;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string CreateMessage(HttpContext context, Exception e)
|
private void SetInternalServerErrorOnResponse(DownstreamContext context)
|
||||||
|
{
|
||||||
|
if (!context.HttpContext.Response.HasStarted)
|
||||||
|
{
|
||||||
|
context.HttpContext.Response.StatusCode = 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string CreateMessage(DownstreamContext context, Exception e)
|
||||||
{
|
{
|
||||||
var message =
|
var message =
|
||||||
$"Exception caught in global error handler, exception message: {e.Message}, exception stack: {e.StackTrace}";
|
$"Exception caught in global error handler, exception message: {e.Message}, exception stack: {e.StackTrace}";
|
||||||
@ -99,7 +100,7 @@ namespace Ocelot.Errors.Middleware
|
|||||||
message =
|
message =
|
||||||
$"{message}, inner exception message {e.InnerException.Message}, inner exception stack {e.InnerException.StackTrace}";
|
$"{message}, inner exception message {e.InnerException.Message}, inner exception stack {e.InnerException.StackTrace}";
|
||||||
}
|
}
|
||||||
return $"{message} RequestId: {context.TraceIdentifier}";
|
return $"{message} RequestId: {context.HttpContext.TraceIdentifier}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Ocelot.Middleware.Pipeline;
|
||||||
|
|
||||||
namespace Ocelot.Errors.Middleware
|
namespace Ocelot.Errors.Middleware
|
||||||
{
|
{
|
||||||
public static class ExceptionHandlerMiddlewareExtensions
|
public static class ExceptionHandlerMiddlewareExtensions
|
||||||
{
|
{
|
||||||
public static IApplicationBuilder UseExceptionHandlerMiddleware(this IApplicationBuilder builder)
|
public static IOcelotPipelineBuilder UseExceptionHandlerMiddleware(this IOcelotPipelineBuilder builder)
|
||||||
{
|
{
|
||||||
return builder.UseMiddleware<ExceptionHandlerMiddleware>();
|
return builder.UseMiddleware<ExceptionHandlerMiddleware>();
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||||
using Ocelot.Infrastructure.RequestData;
|
using Ocelot.Infrastructure.RequestData;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
@ -8,17 +9,15 @@ namespace Ocelot.Headers.Middleware
|
|||||||
{
|
{
|
||||||
public class HttpHeadersTransformationMiddleware : OcelotMiddleware
|
public class HttpHeadersTransformationMiddleware : OcelotMiddleware
|
||||||
{
|
{
|
||||||
private readonly RequestDelegate _next;
|
private readonly OcelotRequestDelegate _next;
|
||||||
private readonly IOcelotLogger _logger;
|
private readonly IOcelotLogger _logger;
|
||||||
private readonly IHttpContextRequestHeaderReplacer _preReplacer;
|
private readonly IHttpContextRequestHeaderReplacer _preReplacer;
|
||||||
private readonly IHttpResponseHeaderReplacer _postReplacer;
|
private readonly IHttpResponseHeaderReplacer _postReplacer;
|
||||||
|
|
||||||
public HttpHeadersTransformationMiddleware(RequestDelegate next,
|
public HttpHeadersTransformationMiddleware(OcelotRequestDelegate next,
|
||||||
IOcelotLoggerFactory loggerFactory,
|
IOcelotLoggerFactory loggerFactory,
|
||||||
IRequestScopedDataRepository requestScopedDataRepository,
|
|
||||||
IHttpContextRequestHeaderReplacer preReplacer,
|
IHttpContextRequestHeaderReplacer preReplacer,
|
||||||
IHttpResponseHeaderReplacer postReplacer)
|
IHttpResponseHeaderReplacer postReplacer)
|
||||||
: base(requestScopedDataRepository)
|
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_postReplacer = postReplacer;
|
_postReplacer = postReplacer;
|
||||||
@ -26,17 +25,18 @@ namespace Ocelot.Headers.Middleware
|
|||||||
_logger = loggerFactory.CreateLogger<HttpHeadersTransformationMiddleware>();
|
_logger = loggerFactory.CreateLogger<HttpHeadersTransformationMiddleware>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Invoke(HttpContext context)
|
public async Task Invoke(DownstreamContext context)
|
||||||
{
|
{
|
||||||
var preFAndRs = this.DownstreamRoute.ReRoute.UpstreamHeadersFindAndReplace;
|
var preFAndRs = context.DownstreamReRoute.UpstreamHeadersFindAndReplace;
|
||||||
|
|
||||||
_preReplacer.Replace(context, preFAndRs);
|
//todo - this should be on httprequestmessage not httpcontext?
|
||||||
|
_preReplacer.Replace(context.HttpContext, preFAndRs);
|
||||||
|
|
||||||
await _next.Invoke(context);
|
await _next.Invoke(context);
|
||||||
|
|
||||||
var postFAndRs = this.DownstreamRoute.ReRoute.DownstreamHeadersFindAndReplace;
|
var postFAndRs = context.DownstreamReRoute.DownstreamHeadersFindAndReplace;
|
||||||
|
|
||||||
_postReplacer.Replace(HttpResponseMessage, postFAndRs, DownstreamRequest);
|
_postReplacer.Replace(context.DownstreamResponse, postFAndRs, context.DownstreamRequest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,10 +1,11 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Ocelot.Middleware.Pipeline;
|
||||||
|
|
||||||
namespace Ocelot.Headers.Middleware
|
namespace Ocelot.Headers.Middleware
|
||||||
{
|
{
|
||||||
public static class HttpHeadersTransformationMiddlewareExtensions
|
public static class HttpHeadersTransformationMiddlewareExtensions
|
||||||
{
|
{
|
||||||
public static IApplicationBuilder UseHttpHeadersTransformationMiddleware(this IApplicationBuilder builder)
|
public static IOcelotPipelineBuilder UseHttpHeadersTransformationMiddleware(this IOcelotPipelineBuilder builder)
|
||||||
{
|
{
|
||||||
return builder.UseMiddleware<HttpHeadersTransformationMiddleware>();
|
return builder.UseMiddleware<HttpHeadersTransformationMiddleware>();
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||||
using Ocelot.Infrastructure.RequestData;
|
using Ocelot.Infrastructure.RequestData;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
@ -9,34 +10,32 @@ namespace Ocelot.Headers.Middleware
|
|||||||
{
|
{
|
||||||
public class HttpRequestHeadersBuilderMiddleware : OcelotMiddleware
|
public class HttpRequestHeadersBuilderMiddleware : OcelotMiddleware
|
||||||
{
|
{
|
||||||
private readonly RequestDelegate _next;
|
private readonly OcelotRequestDelegate _next;
|
||||||
private readonly IAddHeadersToRequest _addHeadersToRequest;
|
private readonly IAddHeadersToRequest _addHeadersToRequest;
|
||||||
private readonly IOcelotLogger _logger;
|
private readonly IOcelotLogger _logger;
|
||||||
|
|
||||||
public HttpRequestHeadersBuilderMiddleware(RequestDelegate next,
|
public HttpRequestHeadersBuilderMiddleware(OcelotRequestDelegate next,
|
||||||
IOcelotLoggerFactory loggerFactory,
|
IOcelotLoggerFactory loggerFactory,
|
||||||
IRequestScopedDataRepository requestScopedDataRepository,
|
|
||||||
IAddHeadersToRequest addHeadersToRequest)
|
IAddHeadersToRequest addHeadersToRequest)
|
||||||
: base(requestScopedDataRepository)
|
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_addHeadersToRequest = addHeadersToRequest;
|
_addHeadersToRequest = addHeadersToRequest;
|
||||||
_logger = loggerFactory.CreateLogger<HttpRequestHeadersBuilderMiddleware>();
|
_logger = loggerFactory.CreateLogger<HttpRequestHeadersBuilderMiddleware>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Invoke(HttpContext context)
|
public async Task Invoke(DownstreamContext context)
|
||||||
{
|
{
|
||||||
if (DownstreamRoute.ReRoute.ClaimsToHeaders.Any())
|
if (context.DownstreamReRoute.ClaimsToHeaders.Any())
|
||||||
{
|
{
|
||||||
_logger.LogDebug($"{ DownstreamRoute.ReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to headers");
|
_logger.LogDebug($"{ context.DownstreamReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to headers");
|
||||||
|
|
||||||
var response = _addHeadersToRequest.SetHeadersOnDownstreamRequest(DownstreamRoute.ReRoute.ClaimsToHeaders, context.User.Claims, DownstreamRequest);
|
var response = _addHeadersToRequest.SetHeadersOnDownstreamRequest(context.DownstreamReRoute.ClaimsToHeaders, context.HttpContext.User.Claims, context.DownstreamRequest);
|
||||||
|
|
||||||
if (response.IsError)
|
if (response.IsError)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Error setting headers on context, setting pipeline error");
|
_logger.LogDebug("Error setting headers on context, setting pipeline error");
|
||||||
|
|
||||||
SetPipelineError(response.Errors);
|
SetPipelineError(context, response.Errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Ocelot.Middleware.Pipeline;
|
||||||
|
|
||||||
namespace Ocelot.Headers.Middleware
|
namespace Ocelot.Headers.Middleware
|
||||||
{
|
{
|
||||||
public static class HttpRequestHeadersBuilderMiddlewareExtensions
|
public static class HttpRequestHeadersBuilderMiddlewareExtensions
|
||||||
{
|
{
|
||||||
public static IApplicationBuilder UseHttpRequestHeadersBuilderMiddleware(this IApplicationBuilder builder)
|
public static IOcelotPipelineBuilder UseHttpRequestHeadersBuilderMiddleware(this IOcelotPipelineBuilder builder)
|
||||||
{
|
{
|
||||||
return builder.UseMiddleware<HttpRequestHeadersBuilderMiddleware>();
|
return builder.UseMiddleware<HttpRequestHeadersBuilderMiddleware>();
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,6 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
|||||||
{
|
{
|
||||||
public interface ILoadBalancerFactory
|
public interface ILoadBalancerFactory
|
||||||
{
|
{
|
||||||
Task<ILoadBalancer> Get(ReRoute reRoute, ServiceProviderConfiguration config);
|
Task<ILoadBalancer> Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,6 +6,6 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
|||||||
{
|
{
|
||||||
public interface ILoadBalancerHouse
|
public interface ILoadBalancerHouse
|
||||||
{
|
{
|
||||||
Task<Response<ILoadBalancer>> Get(ReRoute reRoute, ServiceProviderConfiguration config);
|
Task<Response<ILoadBalancer>> Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -12,7 +12,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
|||||||
_serviceProviderFactory = serviceProviderFactory;
|
_serviceProviderFactory = serviceProviderFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ILoadBalancer> Get(ReRoute reRoute, ServiceProviderConfiguration config)
|
public async Task<ILoadBalancer> Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config)
|
||||||
{
|
{
|
||||||
var serviceProvider = _serviceProviderFactory.Get(config, reRoute);
|
var serviceProvider = _serviceProviderFactory.Get(config, reRoute);
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
|||||||
_loadBalancers = new ConcurrentDictionary<string, ILoadBalancer>();
|
_loadBalancers = new ConcurrentDictionary<string, ILoadBalancer>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Response<ILoadBalancer>> Get(ReRoute reRoute, ServiceProviderConfiguration config)
|
public async Task<Response<ILoadBalancer>> Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||||
using Ocelot.Infrastructure.RequestData;
|
using Ocelot.Infrastructure.RequestData;
|
||||||
using Ocelot.LoadBalancer.LoadBalancers;
|
using Ocelot.LoadBalancer.LoadBalancers;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
@ -11,28 +12,26 @@ namespace Ocelot.LoadBalancer.Middleware
|
|||||||
{
|
{
|
||||||
public class LoadBalancingMiddleware : OcelotMiddleware
|
public class LoadBalancingMiddleware : OcelotMiddleware
|
||||||
{
|
{
|
||||||
private readonly RequestDelegate _next;
|
private readonly OcelotRequestDelegate _next;
|
||||||
private readonly IOcelotLogger _logger;
|
private readonly IOcelotLogger _logger;
|
||||||
private readonly ILoadBalancerHouse _loadBalancerHouse;
|
private readonly ILoadBalancerHouse _loadBalancerHouse;
|
||||||
|
|
||||||
public LoadBalancingMiddleware(RequestDelegate next,
|
public LoadBalancingMiddleware(OcelotRequestDelegate next,
|
||||||
IOcelotLoggerFactory loggerFactory,
|
IOcelotLoggerFactory loggerFactory,
|
||||||
IRequestScopedDataRepository requestScopedDataRepository,
|
|
||||||
ILoadBalancerHouse loadBalancerHouse)
|
ILoadBalancerHouse loadBalancerHouse)
|
||||||
: base(requestScopedDataRepository)
|
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_logger = loggerFactory.CreateLogger<QueryStringBuilderMiddleware>();
|
_logger = loggerFactory.CreateLogger<LoadBalancingMiddleware>();
|
||||||
_loadBalancerHouse = loadBalancerHouse;
|
_loadBalancerHouse = loadBalancerHouse;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Invoke(HttpContext context)
|
public async Task Invoke(DownstreamContext context)
|
||||||
{
|
{
|
||||||
var loadBalancer = await _loadBalancerHouse.Get(DownstreamRoute.ReRoute, ServiceProviderConfiguration);
|
var loadBalancer = await _loadBalancerHouse.Get(context.DownstreamReRoute, context.ServiceProviderConfiguration);
|
||||||
if(loadBalancer.IsError)
|
if(loadBalancer.IsError)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("there was an error retriving the loadbalancer, setting pipeline error");
|
_logger.LogDebug("there was an error retriving the loadbalancer, setting pipeline error");
|
||||||
SetPipelineError(loadBalancer.Errors);
|
SetPipelineError(context, loadBalancer.Errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,11 +39,11 @@ namespace Ocelot.LoadBalancer.Middleware
|
|||||||
if(hostAndPort.IsError)
|
if(hostAndPort.IsError)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("there was an error leasing the loadbalancer, setting pipeline error");
|
_logger.LogDebug("there was an error leasing the loadbalancer, setting pipeline error");
|
||||||
SetPipelineError(hostAndPort.Errors);
|
SetPipelineError(context, hostAndPort.Errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var uriBuilder = new UriBuilder(DownstreamRequest.RequestUri);
|
var uriBuilder = new UriBuilder(context.DownstreamRequest.RequestUri);
|
||||||
|
|
||||||
uriBuilder.Host = hostAndPort.Data.DownstreamHost;
|
uriBuilder.Host = hostAndPort.Data.DownstreamHost;
|
||||||
|
|
||||||
@ -53,7 +52,7 @@ namespace Ocelot.LoadBalancer.Middleware
|
|||||||
uriBuilder.Port = hostAndPort.Data.DownstreamPort;
|
uriBuilder.Port = hostAndPort.Data.DownstreamPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
DownstreamRequest.RequestUri = uriBuilder.Uri;
|
context.DownstreamRequest.RequestUri = uriBuilder.Uri;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Ocelot.Middleware.Pipeline;
|
||||||
|
|
||||||
namespace Ocelot.LoadBalancer.Middleware
|
namespace Ocelot.LoadBalancer.Middleware
|
||||||
{
|
{
|
||||||
public static class LoadBalancingMiddlewareExtensions
|
public static class LoadBalancingMiddlewareExtensions
|
||||||
{
|
{
|
||||||
public static IApplicationBuilder UseLoadBalancingMiddleware(this IApplicationBuilder builder)
|
public static IOcelotPipelineBuilder UseLoadBalancingMiddleware(this IOcelotPipelineBuilder builder)
|
||||||
{
|
{
|
||||||
return builder.UseMiddleware<LoadBalancingMiddleware>();
|
return builder.UseMiddleware<LoadBalancingMiddleware>();
|
||||||
}
|
}
|
||||||
|
29
src/Ocelot/Middleware/DownstreamContext.cs
Normal file
29
src/Ocelot/Middleware/DownstreamContext.cs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Http;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Ocelot.Configuration;
|
||||||
|
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||||
|
using Ocelot.Errors;
|
||||||
|
|
||||||
|
namespace Ocelot.Middleware
|
||||||
|
{
|
||||||
|
public class DownstreamContext
|
||||||
|
{
|
||||||
|
public DownstreamContext(HttpContext httpContext)
|
||||||
|
{
|
||||||
|
this.HttpContext = httpContext;
|
||||||
|
Errors = new List<Error>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PlaceholderNameAndValue> TemplatePlaceholderNameAndValues { get; set; }
|
||||||
|
public ServiceProviderConfiguration ServiceProviderConfiguration {get; set;}
|
||||||
|
public HttpContext HttpContext { get; private set; }
|
||||||
|
public DownstreamReRoute DownstreamReRoute { get; set; }
|
||||||
|
public HttpRequestMessage DownstreamRequest { get; set; }
|
||||||
|
public HttpResponseMessage DownstreamResponse { get; set; }
|
||||||
|
public List<Error> Errors { get;set; }
|
||||||
|
//public string RequestId {get;set;}
|
||||||
|
//public string PreviousRequestId {get;set;}
|
||||||
|
public bool IsError => Errors.Count > 0;
|
||||||
|
}
|
||||||
|
}
|
10
src/Ocelot/Middleware/Multiplexer/IMultiplexer.cs
Normal file
10
src/Ocelot/Middleware/Multiplexer/IMultiplexer.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Ocelot.Configuration;
|
||||||
|
|
||||||
|
namespace Ocelot.Middleware.Multiplexer
|
||||||
|
{
|
||||||
|
public interface IMultiplexer
|
||||||
|
{
|
||||||
|
Task Multiplex(DownstreamContext context, ReRoute reRoute, OcelotRequestDelegate next);
|
||||||
|
}
|
||||||
|
}
|
11
src/Ocelot/Middleware/Multiplexer/IResponseAggregator.cs
Normal file
11
src/Ocelot/Middleware/Multiplexer/IResponseAggregator.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Ocelot.Configuration;
|
||||||
|
|
||||||
|
namespace Ocelot.Middleware.Multiplexer
|
||||||
|
{
|
||||||
|
public interface IResponseAggregator
|
||||||
|
{
|
||||||
|
Task Aggregate(ReRoute reRoute, DownstreamContext originalContext, List<DownstreamContext> downstreamContexts);
|
||||||
|
}
|
||||||
|
}
|
51
src/Ocelot/Middleware/Multiplexer/Multiplexer.cs
Normal file
51
src/Ocelot/Middleware/Multiplexer/Multiplexer.cs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Ocelot.Configuration;
|
||||||
|
|
||||||
|
namespace Ocelot.Middleware.Multiplexer
|
||||||
|
{
|
||||||
|
public class Multiplexer : IMultiplexer
|
||||||
|
{
|
||||||
|
private readonly IResponseAggregator _aggregator;
|
||||||
|
|
||||||
|
public Multiplexer(IResponseAggregator aggregator)
|
||||||
|
{
|
||||||
|
_aggregator = aggregator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Multiplex(DownstreamContext context, ReRoute reRoute, OcelotRequestDelegate next)
|
||||||
|
{
|
||||||
|
var tasks = new Task<DownstreamContext>[reRoute.DownstreamReRoute.Count];
|
||||||
|
|
||||||
|
for (var i = 0; i < reRoute.DownstreamReRoute.Count; i++)
|
||||||
|
{
|
||||||
|
var downstreamContext = new DownstreamContext(context.HttpContext)
|
||||||
|
{
|
||||||
|
TemplatePlaceholderNameAndValues = context.TemplatePlaceholderNameAndValues,
|
||||||
|
ServiceProviderConfiguration = context.ServiceProviderConfiguration,
|
||||||
|
DownstreamReRoute = reRoute.DownstreamReRoute[i],
|
||||||
|
};
|
||||||
|
|
||||||
|
tasks[i] = Fire(downstreamContext, next);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.WhenAll(tasks);
|
||||||
|
|
||||||
|
var downstreamContexts = new List<DownstreamContext>();
|
||||||
|
|
||||||
|
foreach (var task in tasks)
|
||||||
|
{
|
||||||
|
var finished = await task;
|
||||||
|
downstreamContexts.Add(finished);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _aggregator.Aggregate(reRoute, context, downstreamContexts);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<DownstreamContext> Fire(DownstreamContext context, OcelotRequestDelegate next)
|
||||||
|
{
|
||||||
|
await next.Invoke(context);
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Ocelot.Configuration;
|
||||||
|
|
||||||
|
namespace Ocelot.Middleware.Multiplexer
|
||||||
|
{
|
||||||
|
public class SimpleJsonResponseAggregator : IResponseAggregator
|
||||||
|
{
|
||||||
|
public async Task Aggregate(ReRoute reRoute, DownstreamContext originalContext, List<DownstreamContext> downstreamContexts)
|
||||||
|
{
|
||||||
|
if (reRoute.DownstreamReRoute.Count > 1)
|
||||||
|
{
|
||||||
|
await MapAggregtes(originalContext, downstreamContexts);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MapNotAggregate(originalContext, downstreamContexts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task MapAggregtes(DownstreamContext originalContext, List<DownstreamContext> downstreamContexts)
|
||||||
|
{
|
||||||
|
await MapAggregateContent(originalContext, downstreamContexts);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task MapAggregateContent(DownstreamContext originalContext, List<DownstreamContext> downstreamContexts)
|
||||||
|
{
|
||||||
|
var contentBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
contentBuilder.Append("{");
|
||||||
|
|
||||||
|
for (int i = 0; i < downstreamContexts.Count; i++)
|
||||||
|
{
|
||||||
|
if (downstreamContexts[i].IsError)
|
||||||
|
{
|
||||||
|
MapAggregateError(originalContext, downstreamContexts, i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var content = await downstreamContexts[i].DownstreamResponse.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
contentBuilder.Append($"\"{downstreamContexts[i].DownstreamReRoute.Key}\":{content}");
|
||||||
|
|
||||||
|
if (i + 1 < downstreamContexts.Count)
|
||||||
|
{
|
||||||
|
contentBuilder.Append(",");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contentBuilder.Append("}");
|
||||||
|
|
||||||
|
originalContext.DownstreamResponse = new HttpResponseMessage(HttpStatusCode.OK)
|
||||||
|
{
|
||||||
|
Content = new StringContent(contentBuilder.ToString())
|
||||||
|
{
|
||||||
|
Headers = {ContentType = new MediaTypeHeaderValue("application/json")}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void MapAggregateError(DownstreamContext originalContext, List<DownstreamContext> downstreamContexts, int i)
|
||||||
|
{
|
||||||
|
originalContext.Errors.AddRange(downstreamContexts[i].Errors);
|
||||||
|
originalContext.DownstreamResponse = downstreamContexts[i].DownstreamResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MapNotAggregate(DownstreamContext originalContext, List<DownstreamContext> downstreamContexts)
|
||||||
|
{
|
||||||
|
//assume at least one..if this errors then it will be caught by global exception handler
|
||||||
|
var finished = downstreamContexts.First();
|
||||||
|
|
||||||
|
originalContext.Errors = finished.Errors;
|
||||||
|
|
||||||
|
originalContext.DownstreamRequest = finished.DownstreamRequest;
|
||||||
|
|
||||||
|
originalContext.DownstreamResponse = finished.DownstreamResponse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,67 +1,20 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net.Http;
|
|
||||||
using Ocelot.Configuration;
|
|
||||||
using Ocelot.DownstreamRouteFinder;
|
|
||||||
using Ocelot.Errors;
|
using Ocelot.Errors;
|
||||||
using Ocelot.Infrastructure.RequestData;
|
|
||||||
|
|
||||||
namespace Ocelot.Middleware
|
namespace Ocelot.Middleware
|
||||||
{
|
{
|
||||||
public abstract class OcelotMiddleware
|
public abstract class OcelotMiddleware
|
||||||
{
|
{
|
||||||
private readonly IRequestScopedDataRepository _requestScopedDataRepository;
|
protected OcelotMiddleware()
|
||||||
|
|
||||||
protected OcelotMiddleware(IRequestScopedDataRepository requestScopedDataRepository)
|
|
||||||
{
|
{
|
||||||
_requestScopedDataRepository = requestScopedDataRepository;
|
|
||||||
MiddlewareName = this.GetType().Name;
|
MiddlewareName = this.GetType().Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string MiddlewareName { get; }
|
public string MiddlewareName { get; }
|
||||||
|
|
||||||
public bool PipelineError => _requestScopedDataRepository.Get<bool>("OcelotMiddlewareError").Data;
|
public void SetPipelineError(DownstreamContext context, List<Error> errors)
|
||||||
|
|
||||||
public List<Error> PipelineErrors => _requestScopedDataRepository.Get<List<Error>>("OcelotMiddlewareErrors").Data;
|
|
||||||
|
|
||||||
public DownstreamRoute DownstreamRoute => _requestScopedDataRepository.Get<DownstreamRoute>("DownstreamRoute").Data;
|
|
||||||
|
|
||||||
public Request.Request Request => _requestScopedDataRepository.Get<Request.Request>("Request").Data;
|
|
||||||
|
|
||||||
public HttpRequestMessage DownstreamRequest => _requestScopedDataRepository.Get<HttpRequestMessage>("DownstreamRequest").Data;
|
|
||||||
|
|
||||||
public HttpResponseMessage HttpResponseMessage => _requestScopedDataRepository.Get<HttpResponseMessage>("HttpResponseMessage").Data;
|
|
||||||
|
|
||||||
public ServiceProviderConfiguration ServiceProviderConfiguration => _requestScopedDataRepository.Get<ServiceProviderConfiguration>("ServiceProviderConfiguration").Data;
|
|
||||||
|
|
||||||
public void SetDownstreamRouteForThisRequest(DownstreamRoute downstreamRoute)
|
|
||||||
{
|
{
|
||||||
_requestScopedDataRepository.Add("DownstreamRoute", downstreamRoute);
|
context.Errors = errors;
|
||||||
}
|
|
||||||
|
|
||||||
public void SetServiceProviderConfigurationForThisRequest(ServiceProviderConfiguration serviceProviderConfiguration)
|
|
||||||
{
|
|
||||||
_requestScopedDataRepository.Add("ServiceProviderConfiguration", serviceProviderConfiguration);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetUpstreamRequestForThisRequest(Request.Request request)
|
|
||||||
{
|
|
||||||
_requestScopedDataRepository.Add("Request", request);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetDownstreamRequest(HttpRequestMessage request)
|
|
||||||
{
|
|
||||||
_requestScopedDataRepository.Add("DownstreamRequest", request);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetHttpResponseMessageThisRequest(HttpResponseMessage responseMessage)
|
|
||||||
{
|
|
||||||
_requestScopedDataRepository.Add("HttpResponseMessage", responseMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetPipelineError(List<Error> errors)
|
|
||||||
{
|
|
||||||
_requestScopedDataRepository.Add("OcelotMiddlewareError", true);
|
|
||||||
_requestScopedDataRepository.Add("OcelotMiddlewareErrors", errors);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Authorisation.Middleware;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
@ -15,24 +14,11 @@
|
|||||||
using Ocelot.Configuration.Provider;
|
using Ocelot.Configuration.Provider;
|
||||||
using Ocelot.Configuration.Repository;
|
using Ocelot.Configuration.Repository;
|
||||||
using Ocelot.Configuration.Setter;
|
using Ocelot.Configuration.Setter;
|
||||||
using Ocelot.LoadBalancer.Middleware;
|
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using Ocelot.Authentication.Middleware;
|
|
||||||
using Ocelot.Cache.Middleware;
|
|
||||||
using Ocelot.Claims.Middleware;
|
|
||||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
|
||||||
using Ocelot.DownstreamUrlCreator.Middleware;
|
|
||||||
using Ocelot.Errors.Middleware;
|
|
||||||
using Ocelot.Headers.Middleware;
|
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.QueryStrings.Middleware;
|
|
||||||
using Ocelot.Request.Middleware;
|
|
||||||
using Ocelot.Requester.Middleware;
|
|
||||||
using Ocelot.RequestId.Middleware;
|
|
||||||
using Ocelot.Responder.Middleware;
|
|
||||||
using Ocelot.RateLimit.Middleware;
|
|
||||||
using Rafty.Concensus;
|
using Rafty.Concensus;
|
||||||
using Rafty.Infrastructure;
|
using Rafty.Infrastructure;
|
||||||
|
using Ocelot.Middleware.Pipeline;
|
||||||
|
|
||||||
public static class OcelotMiddlewareExtensions
|
public static class OcelotMiddlewareExtensions
|
||||||
{
|
{
|
||||||
@ -43,7 +29,7 @@
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder)
|
public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder)
|
||||||
{
|
{
|
||||||
await builder.UseOcelot(new OcelotMiddlewareConfiguration());
|
await builder.UseOcelot(new OcelotPipelineConfiguration());
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
@ -52,9 +38,9 @@
|
|||||||
/// Registers Ocelot with a combination of default middlewares and optional middlewares in the configuration
|
/// Registers Ocelot with a combination of default middlewares and optional middlewares in the configuration
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="builder"></param>
|
/// <param name="builder"></param>
|
||||||
/// <param name="middlewareConfiguration"></param>
|
/// <param name="pipelineConfiguration"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder, OcelotMiddlewareConfiguration middlewareConfiguration)
|
public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration)
|
||||||
{
|
{
|
||||||
var configuration = await CreateConfiguration(builder);
|
var configuration = await CreateConfiguration(builder);
|
||||||
|
|
||||||
@ -67,91 +53,21 @@
|
|||||||
|
|
||||||
ConfigureDiagnosticListener(builder);
|
ConfigureDiagnosticListener(builder);
|
||||||
|
|
||||||
// This is registered to catch any global exceptions that are not handled
|
var pipelineBuilder = new OcelotPipelineBuilder(builder.ApplicationServices);
|
||||||
// It also sets the Request Id if anything is set globally
|
|
||||||
builder.UseExceptionHandlerMiddleware();
|
|
||||||
|
|
||||||
// Allow the user to respond with absolutely anything they want.
|
pipelineBuilder.BuildOcelotPipeline(pipelineConfiguration);
|
||||||
builder.UseIfNotNull(middlewareConfiguration.PreErrorResponderMiddleware);
|
|
||||||
|
|
||||||
// This is registered first so it can catch any errors and issue an appropriate response
|
var firstDelegate = pipelineBuilder.Build();
|
||||||
builder.UseResponderMiddleware();
|
|
||||||
|
|
||||||
// Then we get the downstream route information
|
//inject first delegate into first piece of asp.net middleware..maybe not like this
|
||||||
builder.UseDownstreamRouteFinderMiddleware();
|
//then because we are updating the http context in ocelot it comes out correct for
|
||||||
|
//rest of asp.net..
|
||||||
|
|
||||||
// Now we have the ds route we can transform headers and stuff?
|
builder.Use(async (context, task) =>
|
||||||
builder.UseHttpHeadersTransformationMiddleware();
|
|
||||||
|
|
||||||
// Initialises downstream request
|
|
||||||
builder.UseDownstreamRequestInitialiser();
|
|
||||||
|
|
||||||
// We check whether the request is ratelimit, and if there is no continue processing
|
|
||||||
builder.UseRateLimiting();
|
|
||||||
|
|
||||||
// This adds or updates the request id (initally we try and set this based on global config in the error handling middleware)
|
|
||||||
// If anything was set at global level and we have a different setting at re route level the global stuff will be overwritten
|
|
||||||
// This means you can get a scenario where you have a different request id from the first piece of middleware to the request id middleware.
|
|
||||||
builder.UseRequestIdMiddleware();
|
|
||||||
|
|
||||||
// Allow pre authentication logic. The idea being people might want to run something custom before what is built in.
|
|
||||||
builder.UseIfNotNull(middlewareConfiguration.PreAuthenticationMiddleware);
|
|
||||||
|
|
||||||
// Now we know where the client is going to go we can authenticate them.
|
|
||||||
// We allow the ocelot middleware to be overriden by whatever the
|
|
||||||
// user wants
|
|
||||||
if (middlewareConfiguration.AuthenticationMiddleware == null)
|
|
||||||
{
|
{
|
||||||
builder.UseAuthenticationMiddleware();
|
var downstreamContext = new DownstreamContext(context);
|
||||||
}
|
await firstDelegate.Invoke(downstreamContext);
|
||||||
else
|
});
|
||||||
{
|
|
||||||
builder.Use(middlewareConfiguration.AuthenticationMiddleware);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The next thing we do is look at any claims transforms in case this is important for authorisation
|
|
||||||
builder.UseClaimsBuilderMiddleware();
|
|
||||||
|
|
||||||
// Allow pre authorisation logic. The idea being people might want to run something custom before what is built in.
|
|
||||||
builder.UseIfNotNull(middlewareConfiguration.PreAuthorisationMiddleware);
|
|
||||||
|
|
||||||
// Now we have authenticated and done any claims transformation we
|
|
||||||
// can authorise the request
|
|
||||||
// We allow the ocelot middleware to be overriden by whatever the
|
|
||||||
// user wants
|
|
||||||
if (middlewareConfiguration.AuthorisationMiddleware == null)
|
|
||||||
{
|
|
||||||
builder.UseAuthorisationMiddleware();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
builder.Use(middlewareConfiguration.AuthorisationMiddleware);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we can run any header transformation logic
|
|
||||||
builder.UseHttpRequestHeadersBuilderMiddleware();
|
|
||||||
|
|
||||||
// Allow the user to implement their own query string manipulation logic
|
|
||||||
builder.UseIfNotNull(middlewareConfiguration.PreQueryStringBuilderMiddleware);
|
|
||||||
|
|
||||||
// Now we can run any query string transformation logic
|
|
||||||
builder.UseQueryStringBuilderMiddleware();
|
|
||||||
|
|
||||||
// Get the load balancer for this request
|
|
||||||
builder.UseLoadBalancingMiddleware();
|
|
||||||
|
|
||||||
// This takes the downstream route we retrieved earlier and replaces any placeholders with the variables that should be used
|
|
||||||
builder.UseDownstreamUrlCreatorMiddleware();
|
|
||||||
|
|
||||||
// Not sure if this is the best place for this but we use the downstream url
|
|
||||||
// as the basis for our cache key.
|
|
||||||
builder.UseOutputCacheMiddleware();
|
|
||||||
|
|
||||||
// Everything should now be ready to build or HttpRequest
|
|
||||||
builder.UseHttpRequestBuilderMiddleware();
|
|
||||||
|
|
||||||
//We fire off the request and set the response on the scoped data repo
|
|
||||||
builder.UseHttpRequesterMiddleware();
|
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
@ -257,23 +173,34 @@
|
|||||||
var ocelotConfigurationRepository =
|
var ocelotConfigurationRepository =
|
||||||
(IOcelotConfigurationRepository) builder.ApplicationServices.GetService(
|
(IOcelotConfigurationRepository) builder.ApplicationServices.GetService(
|
||||||
typeof(IOcelotConfigurationRepository));
|
typeof(IOcelotConfigurationRepository));
|
||||||
|
|
||||||
var ocelotConfigurationCreator =
|
var ocelotConfigurationCreator =
|
||||||
(IOcelotConfigurationCreator) builder.ApplicationServices.GetService(
|
(IOcelotConfigurationCreator) builder.ApplicationServices.GetService(
|
||||||
typeof(IOcelotConfigurationCreator));
|
typeof(IOcelotConfigurationCreator));
|
||||||
|
|
||||||
var fileConfigFromConsul = await consulFileConfigRepo.Get();
|
var fileConfigFromConsul = await consulFileConfigRepo.Get();
|
||||||
|
|
||||||
if (fileConfigFromConsul.Data == null)
|
if (fileConfigFromConsul.Data == null)
|
||||||
{
|
{
|
||||||
config = await setter.Set(fileConfig.Value);
|
config = await setter.Set(fileConfig.Value);
|
||||||
|
var hack = builder.ApplicationServices.GetService(typeof(ConsulFileConfigurationPoller));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var ocelotConfig = await ocelotConfigurationCreator.Create(fileConfigFromConsul.Data);
|
var ocelotConfig = await ocelotConfigurationCreator.Create(fileConfigFromConsul.Data);
|
||||||
|
|
||||||
if(ocelotConfig.IsError)
|
if(ocelotConfig.IsError)
|
||||||
{
|
{
|
||||||
return new ErrorResponse(ocelotConfig.Errors);
|
return new ErrorResponse(ocelotConfig.Errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
config = await ocelotConfigurationRepository.AddOrReplace(ocelotConfig.Data);
|
config = await ocelotConfigurationRepository.AddOrReplace(ocelotConfig.Data);
|
||||||
|
|
||||||
|
if (config.IsError)
|
||||||
|
{
|
||||||
|
return new ErrorResponse(config.Errors);
|
||||||
|
}
|
||||||
|
|
||||||
//todo - this starts the poller if it has been registered...please this is so bad.
|
//todo - this starts the poller if it has been registered...please this is so bad.
|
||||||
var hack = builder.ApplicationServices.GetService(typeof(ConsulFileConfigurationPoller));
|
var hack = builder.ApplicationServices.GetService(typeof(ConsulFileConfigurationPoller));
|
||||||
}
|
}
|
||||||
|
@ -2,43 +2,42 @@
|
|||||||
{
|
{
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
|
|
||||||
public class OcelotMiddlewareConfiguration
|
public class OcelotPipelineConfiguration
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is called after the global error handling middleware so any code before calling next.invoke
|
/// This is called after the global error handling middleware so any code before calling next.invoke
|
||||||
/// is the next thing called in the Ocelot pipeline. Anything after next.invoke is the last thing called
|
/// is the next thing called in the Ocelot pipeline. Anything after next.invoke is the last thing called
|
||||||
/// in the Ocelot pipeline before we go to the global error handler.
|
/// in the Ocelot pipeline before we go to the global error handler.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<HttpContext, Func<Task>, Task> PreErrorResponderMiddleware { get; set; }
|
public Func<DownstreamContext, Func<Task>, Task> PreErrorResponderMiddleware { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is to allow the user to run any extra authentication before the Ocelot authentication
|
/// This is to allow the user to run any extra authentication before the Ocelot authentication
|
||||||
/// kicks in
|
/// kicks in
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<HttpContext, Func<Task>, Task> PreAuthenticationMiddleware { get; set; }
|
public Func<DownstreamContext, Func<Task>, Task> PreAuthenticationMiddleware { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This allows the user to completely override the ocelot authentication middleware
|
/// This allows the user to completely override the ocelot authentication middleware
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<HttpContext, Func<Task>, Task> AuthenticationMiddleware { get; set; }
|
public Func<DownstreamContext, Func<Task>, Task> AuthenticationMiddleware { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is to allow the user to run any extra authorisation before the Ocelot authentication
|
/// This is to allow the user to run any extra authorisation before the Ocelot authentication
|
||||||
/// kicks in
|
/// kicks in
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<HttpContext, Func<Task>, Task> PreAuthorisationMiddleware { get; set; }
|
public Func<DownstreamContext, Func<Task>, Task> PreAuthorisationMiddleware { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This allows the user to completely override the ocelot authorisation middleware
|
/// This allows the user to completely override the ocelot authorisation middleware
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<HttpContext, Func<Task>, Task> AuthorisationMiddleware { get; set; }
|
public Func<DownstreamContext, Func<Task>, Task> AuthorisationMiddleware { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This allows the user to implement there own query string manipulation logic
|
/// This allows the user to implement there own query string manipulation logic
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<HttpContext, Func<Task>, Task> PreQueryStringBuilderMiddleware { get; set; }
|
public Func<DownstreamContext, Func<Task>, Task> PreQueryStringBuilderMiddleware { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
6
src/Ocelot/Middleware/OcelotRequestDelegate.cs
Normal file
6
src/Ocelot/Middleware/OcelotRequestDelegate.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ocelot.Middleware
|
||||||
|
{
|
||||||
|
public delegate Task OcelotRequestDelegate(DownstreamContext downstreamContext);
|
||||||
|
}
|
15
src/Ocelot/Middleware/Pipeline/IOcelotPipelineBuilder.cs
Normal file
15
src/Ocelot/Middleware/Pipeline/IOcelotPipelineBuilder.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
// Removed code and changed RequestDelete to OcelotRequestDelete, HttpContext to DownstreamContext, removed some exception handling messages
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ocelot.Middleware.Pipeline
|
||||||
|
{
|
||||||
|
public interface IOcelotPipelineBuilder
|
||||||
|
{
|
||||||
|
IServiceProvider ApplicationServices { get; }
|
||||||
|
OcelotPipelineBuilder Use(Func<OcelotRequestDelegate, OcelotRequestDelegate> middleware);
|
||||||
|
OcelotRequestDelegate Build();
|
||||||
|
}
|
||||||
|
}
|
14
src/Ocelot/Middleware/Pipeline/LICENSE.txt
Normal file
14
src/Ocelot/Middleware/Pipeline/LICENSE.txt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
Copyright (c) .NET Foundation and Contributors
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
46
src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilder.cs
Normal file
46
src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilder.cs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
// Removed code and changed RequestDelete to OcelotRequestDelete, HttpContext to DownstreamContext, removed some exception handling messages
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ocelot.Middleware.Pipeline
|
||||||
|
{
|
||||||
|
public class OcelotPipelineBuilder : IOcelotPipelineBuilder
|
||||||
|
{
|
||||||
|
private readonly IList<Func<OcelotRequestDelegate, OcelotRequestDelegate>> _middlewares;
|
||||||
|
|
||||||
|
public OcelotPipelineBuilder(IServiceProvider provider)
|
||||||
|
{
|
||||||
|
ApplicationServices = provider;
|
||||||
|
_middlewares = new List<Func<OcelotRequestDelegate, OcelotRequestDelegate>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IServiceProvider ApplicationServices { get; }
|
||||||
|
|
||||||
|
public OcelotPipelineBuilder Use(Func<OcelotRequestDelegate, OcelotRequestDelegate> middleware)
|
||||||
|
{
|
||||||
|
_middlewares.Add(middleware);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OcelotRequestDelegate Build()
|
||||||
|
{
|
||||||
|
OcelotRequestDelegate app = context =>
|
||||||
|
{
|
||||||
|
context.HttpContext.Response.StatusCode = 404;
|
||||||
|
return Task.CompletedTask;
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var component in _middlewares.Reverse())
|
||||||
|
{
|
||||||
|
app = component(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,146 @@
|
|||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
// Removed code and changed RequestDelete to OcelotRequestDelete, HttpContext to DownstreamContext, removed some exception handling messages
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Ocelot.Middleware.Pipeline
|
||||||
|
{
|
||||||
|
public static class OcelotPipelineBuilderExtensions
|
||||||
|
{
|
||||||
|
internal const string InvokeMethodName = "Invoke";
|
||||||
|
internal const string InvokeAsyncMethodName = "InvokeAsync";
|
||||||
|
private static readonly MethodInfo GetServiceInfo = typeof(OcelotPipelineBuilderExtensions).GetMethod(nameof(GetService), BindingFlags.NonPublic | BindingFlags.Static);
|
||||||
|
|
||||||
|
public static IOcelotPipelineBuilder UseMiddleware<TMiddleware>(this IOcelotPipelineBuilder app, params object[] args)
|
||||||
|
{
|
||||||
|
return app.UseMiddleware(typeof(TMiddleware), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IOcelotPipelineBuilder Use(this IOcelotPipelineBuilder app, Func<DownstreamContext, Func<Task>, Task> middleware)
|
||||||
|
{
|
||||||
|
return app.Use(next =>
|
||||||
|
{
|
||||||
|
return context =>
|
||||||
|
{
|
||||||
|
Func<Task> simpleNext = () => next(context);
|
||||||
|
return middleware(context, simpleNext);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IOcelotPipelineBuilder UseMiddleware(this IOcelotPipelineBuilder app, Type middleware, params object[] args)
|
||||||
|
{
|
||||||
|
return app.Use(next =>
|
||||||
|
{
|
||||||
|
var methods = middleware.GetMethods(BindingFlags.Instance | BindingFlags.Public);
|
||||||
|
var invokeMethods = methods.Where(m =>
|
||||||
|
string.Equals(m.Name, InvokeMethodName, StringComparison.Ordinal)
|
||||||
|
|| string.Equals(m.Name, InvokeAsyncMethodName, StringComparison.Ordinal)
|
||||||
|
).ToArray();
|
||||||
|
|
||||||
|
if (invokeMethods.Length > 1)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invokeMethods.Length == 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var methodinfo = invokeMethods[0];
|
||||||
|
if (!typeof(Task).IsAssignableFrom(methodinfo.ReturnType))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var parameters = methodinfo.GetParameters();
|
||||||
|
if (parameters.Length == 0 || parameters[0].ParameterType != typeof(DownstreamContext))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var ctorArgs = new object[args.Length + 1];
|
||||||
|
ctorArgs[0] = next;
|
||||||
|
Array.Copy(args, 0, ctorArgs, 1, args.Length);
|
||||||
|
var instance = ActivatorUtilities.CreateInstance(app.ApplicationServices, middleware, ctorArgs);
|
||||||
|
if (parameters.Length == 1)
|
||||||
|
{
|
||||||
|
return (OcelotRequestDelegate)methodinfo.CreateDelegate(typeof(OcelotRequestDelegate), instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
var factory = Compile<object>(methodinfo, parameters);
|
||||||
|
|
||||||
|
return context =>
|
||||||
|
{
|
||||||
|
var serviceProvider = context.HttpContext.RequestServices ?? app.ApplicationServices;
|
||||||
|
if (serviceProvider == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return factory(instance, context, serviceProvider);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Func<T, DownstreamContext, IServiceProvider, Task> Compile<T>(MethodInfo methodinfo, ParameterInfo[] parameters)
|
||||||
|
{
|
||||||
|
var middleware = typeof(T);
|
||||||
|
var httpContextArg = Expression.Parameter(typeof(HttpContext), "httpContext");
|
||||||
|
var providerArg = Expression.Parameter(typeof(IServiceProvider), "serviceProvider");
|
||||||
|
var instanceArg = Expression.Parameter(middleware, "middleware");
|
||||||
|
|
||||||
|
var methodArguments = new Expression[parameters.Length];
|
||||||
|
methodArguments[0] = httpContextArg;
|
||||||
|
for (int i = 1; i < parameters.Length; i++)
|
||||||
|
{
|
||||||
|
var parameterType = parameters[i].ParameterType;
|
||||||
|
if (parameterType.IsByRef)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var parameterTypeExpression = new Expression[]
|
||||||
|
{
|
||||||
|
providerArg,
|
||||||
|
Expression.Constant(parameterType, typeof(Type)),
|
||||||
|
Expression.Constant(methodinfo.DeclaringType, typeof(Type))
|
||||||
|
};
|
||||||
|
|
||||||
|
var getServiceCall = Expression.Call(GetServiceInfo, parameterTypeExpression);
|
||||||
|
methodArguments[i] = Expression.Convert(getServiceCall, parameterType);
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression middlewareInstanceArg = instanceArg;
|
||||||
|
if (methodinfo.DeclaringType != typeof(T))
|
||||||
|
{
|
||||||
|
middlewareInstanceArg = Expression.Convert(middlewareInstanceArg, methodinfo.DeclaringType);
|
||||||
|
}
|
||||||
|
|
||||||
|
var body = Expression.Call(middlewareInstanceArg, methodinfo, methodArguments);
|
||||||
|
|
||||||
|
var lambda = Expression.Lambda<Func<T, DownstreamContext, IServiceProvider, Task>>(body, instanceArg, httpContextArg, providerArg);
|
||||||
|
|
||||||
|
return lambda.Compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object GetService(IServiceProvider sp, Type type)
|
||||||
|
{
|
||||||
|
var service = sp.GetService(type);
|
||||||
|
if (service == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
121
src/Ocelot/Middleware/Pipeline/OcelotPipelineExtensions.cs
Normal file
121
src/Ocelot/Middleware/Pipeline/OcelotPipelineExtensions.cs
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Ocelot.Authentication.Middleware;
|
||||||
|
using Ocelot.Authorisation.Middleware;
|
||||||
|
using Ocelot.Cache.Middleware;
|
||||||
|
using Ocelot.Claims.Middleware;
|
||||||
|
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||||
|
using Ocelot.DownstreamUrlCreator.Middleware;
|
||||||
|
using Ocelot.Errors.Middleware;
|
||||||
|
using Ocelot.Headers.Middleware;
|
||||||
|
using Ocelot.LoadBalancer.Middleware;
|
||||||
|
using Ocelot.QueryStrings.Middleware;
|
||||||
|
using Ocelot.RateLimit.Middleware;
|
||||||
|
using Ocelot.Request.Middleware;
|
||||||
|
using Ocelot.Requester.Middleware;
|
||||||
|
using Ocelot.RequestId.Middleware;
|
||||||
|
using Ocelot.Responder.Middleware;
|
||||||
|
|
||||||
|
namespace Ocelot.Middleware.Pipeline
|
||||||
|
{
|
||||||
|
public static class OcelotPipelineExtensions
|
||||||
|
{
|
||||||
|
public static OcelotRequestDelegate BuildOcelotPipeline(this IOcelotPipelineBuilder builder,
|
||||||
|
OcelotPipelineConfiguration pipelineConfiguration = null)
|
||||||
|
{
|
||||||
|
// This is registered to catch any global exceptions that are not handled
|
||||||
|
// It also sets the Request Id if anything is set globally
|
||||||
|
builder.UseExceptionHandlerMiddleware();
|
||||||
|
|
||||||
|
// Allow the user to respond with absolutely anything they want.
|
||||||
|
builder.UseIfNotNull(pipelineConfiguration.PreErrorResponderMiddleware);
|
||||||
|
|
||||||
|
// This is registered first so it can catch any errors and issue an appropriate response
|
||||||
|
builder.UseResponderMiddleware();
|
||||||
|
|
||||||
|
// Then we get the downstream route information
|
||||||
|
builder.UseDownstreamRouteFinderMiddleware();
|
||||||
|
|
||||||
|
// Now we have the ds route we can transform headers and stuff?
|
||||||
|
builder.UseHttpHeadersTransformationMiddleware();
|
||||||
|
|
||||||
|
// Initialises downstream request
|
||||||
|
builder.UseDownstreamRequestInitialiser();
|
||||||
|
|
||||||
|
// We check whether the request is ratelimit, and if there is no continue processing
|
||||||
|
builder.UseRateLimiting();
|
||||||
|
|
||||||
|
// This adds or updates the request id (initally we try and set this based on global config in the error handling middleware)
|
||||||
|
// If anything was set at global level and we have a different setting at re route level the global stuff will be overwritten
|
||||||
|
// This means you can get a scenario where you have a different request id from the first piece of middleware to the request id middleware.
|
||||||
|
builder.UseRequestIdMiddleware();
|
||||||
|
|
||||||
|
// Allow pre authentication logic. The idea being people might want to run something custom before what is built in.
|
||||||
|
builder.UseIfNotNull(pipelineConfiguration.PreAuthenticationMiddleware);
|
||||||
|
|
||||||
|
// Now we know where the client is going to go we can authenticate them.
|
||||||
|
// We allow the ocelot middleware to be overriden by whatever the
|
||||||
|
// user wants
|
||||||
|
if (pipelineConfiguration.AuthenticationMiddleware == null)
|
||||||
|
{
|
||||||
|
builder.UseAuthenticationMiddleware();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
builder.Use(pipelineConfiguration.AuthenticationMiddleware);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The next thing we do is look at any claims transforms in case this is important for authorisation
|
||||||
|
builder.UseClaimsBuilderMiddleware();
|
||||||
|
|
||||||
|
// Allow pre authorisation logic. The idea being people might want to run something custom before what is built in.
|
||||||
|
builder.UseIfNotNull(pipelineConfiguration.PreAuthorisationMiddleware);
|
||||||
|
|
||||||
|
// Now we have authenticated and done any claims transformation we
|
||||||
|
// can authorise the request
|
||||||
|
// We allow the ocelot middleware to be overriden by whatever the
|
||||||
|
// user wants
|
||||||
|
if (pipelineConfiguration.AuthorisationMiddleware == null)
|
||||||
|
{
|
||||||
|
builder.UseAuthorisationMiddleware();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
builder.Use(pipelineConfiguration.AuthorisationMiddleware);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we can run any header transformation logic
|
||||||
|
builder.UseHttpRequestHeadersBuilderMiddleware();
|
||||||
|
|
||||||
|
// Allow the user to implement their own query string manipulation logic
|
||||||
|
builder.UseIfNotNull(pipelineConfiguration.PreQueryStringBuilderMiddleware);
|
||||||
|
|
||||||
|
// Now we can run any query string transformation logic
|
||||||
|
builder.UseQueryStringBuilderMiddleware();
|
||||||
|
|
||||||
|
// Get the load balancer for this request
|
||||||
|
builder.UseLoadBalancingMiddleware();
|
||||||
|
|
||||||
|
// This takes the downstream route we retrieved earlier and replaces any placeholders with the variables that should be used
|
||||||
|
builder.UseDownstreamUrlCreatorMiddleware();
|
||||||
|
|
||||||
|
// Not sure if this is the best place for this but we use the downstream url
|
||||||
|
// as the basis for our cache key.
|
||||||
|
builder.UseOutputCacheMiddleware();
|
||||||
|
|
||||||
|
//We fire off the request and set the response on the scoped data repo
|
||||||
|
builder.UseHttpRequesterMiddleware();
|
||||||
|
|
||||||
|
return builder.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UseIfNotNull(this IOcelotPipelineBuilder builder,
|
||||||
|
Func<DownstreamContext, Func<Task>, Task> middleware)
|
||||||
|
{
|
||||||
|
if (middleware != null)
|
||||||
|
{
|
||||||
|
builder.Use(middleware);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -23,7 +23,7 @@
|
|||||||
<DebugSymbols>True</DebugSymbols>
|
<DebugSymbols>True</DebugSymbols>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Butterfly.Client.AspNetCore" Version="0.0.5" />
|
<PackageReference Include="Butterfly.Client.AspNetCore" Version="0.0.8" />
|
||||||
<PackageReference Include="FluentValidation" Version="7.2.1" />
|
<PackageReference Include="FluentValidation" Version="7.2.1" />
|
||||||
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="2.1.0" />
|
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="2.1.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
|
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||||
using Ocelot.Infrastructure.RequestData;
|
using Ocelot.Infrastructure.RequestData;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
@ -9,34 +10,32 @@ namespace Ocelot.QueryStrings.Middleware
|
|||||||
{
|
{
|
||||||
public class QueryStringBuilderMiddleware : OcelotMiddleware
|
public class QueryStringBuilderMiddleware : OcelotMiddleware
|
||||||
{
|
{
|
||||||
private readonly RequestDelegate _next;
|
private readonly OcelotRequestDelegate _next;
|
||||||
private readonly IAddQueriesToRequest _addQueriesToRequest;
|
private readonly IAddQueriesToRequest _addQueriesToRequest;
|
||||||
private readonly IOcelotLogger _logger;
|
private readonly IOcelotLogger _logger;
|
||||||
|
|
||||||
public QueryStringBuilderMiddleware(RequestDelegate next,
|
public QueryStringBuilderMiddleware(OcelotRequestDelegate next,
|
||||||
IOcelotLoggerFactory loggerFactory,
|
IOcelotLoggerFactory loggerFactory,
|
||||||
IRequestScopedDataRepository requestScopedDataRepository,
|
|
||||||
IAddQueriesToRequest addQueriesToRequest)
|
IAddQueriesToRequest addQueriesToRequest)
|
||||||
: base(requestScopedDataRepository)
|
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_addQueriesToRequest = addQueriesToRequest;
|
_addQueriesToRequest = addQueriesToRequest;
|
||||||
_logger = loggerFactory.CreateLogger<QueryStringBuilderMiddleware>();
|
_logger = loggerFactory.CreateLogger<QueryStringBuilderMiddleware>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Invoke(HttpContext context)
|
public async Task Invoke(DownstreamContext context)
|
||||||
{
|
{
|
||||||
if (DownstreamRoute.ReRoute.ClaimsToQueries.Any())
|
if (context.DownstreamReRoute.ClaimsToQueries.Any())
|
||||||
{
|
{
|
||||||
_logger.LogDebug($"{DownstreamRoute.ReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to queries");
|
_logger.LogDebug($"{context.DownstreamReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to queries");
|
||||||
|
|
||||||
var response = _addQueriesToRequest.SetQueriesOnDownstreamRequest(DownstreamRoute.ReRoute.ClaimsToQueries, context.User.Claims, DownstreamRequest);
|
var response = _addQueriesToRequest.SetQueriesOnDownstreamRequest(context.DownstreamReRoute.ClaimsToQueries, context.HttpContext.User.Claims, context.DownstreamRequest);
|
||||||
|
|
||||||
if (response.IsError)
|
if (response.IsError)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("there was an error setting queries on context, setting pipeline error");
|
_logger.LogDebug("there was an error setting queries on context, setting pipeline error");
|
||||||
|
|
||||||
SetPipelineError(response.Errors);
|
SetPipelineError(context, response.Errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Ocelot.Middleware.Pipeline;
|
||||||
|
|
||||||
namespace Ocelot.QueryStrings.Middleware
|
namespace Ocelot.QueryStrings.Middleware
|
||||||
{
|
{
|
||||||
public static class QueryStringBuilderMiddlewareExtensions
|
public static class QueryStringBuilderMiddlewareExtensions
|
||||||
{
|
{
|
||||||
public static IApplicationBuilder UseQueryStringBuilderMiddleware(this IApplicationBuilder builder)
|
public static IOcelotPipelineBuilder UseQueryStringBuilderMiddleware(this IOcelotPipelineBuilder builder)
|
||||||
{
|
{
|
||||||
return builder.UseMiddleware<QueryStringBuilderMiddleware>();
|
return builder.UseMiddleware<QueryStringBuilderMiddleware>();
|
||||||
}
|
}
|
||||||
|
@ -7,21 +7,20 @@ using Ocelot.Infrastructure.RequestData;
|
|||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Configuration;
|
using Ocelot.Configuration;
|
||||||
|
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||||
|
|
||||||
namespace Ocelot.RateLimit.Middleware
|
namespace Ocelot.RateLimit.Middleware
|
||||||
{
|
{
|
||||||
public class ClientRateLimitMiddleware : OcelotMiddleware
|
public class ClientRateLimitMiddleware : OcelotMiddleware
|
||||||
{
|
{
|
||||||
private readonly RequestDelegate _next;
|
private readonly OcelotRequestDelegate _next;
|
||||||
private readonly IOcelotLogger _logger;
|
private readonly IOcelotLogger _logger;
|
||||||
private readonly IRateLimitCounterHandler _counterHandler;
|
private readonly IRateLimitCounterHandler _counterHandler;
|
||||||
private readonly ClientRateLimitProcessor _processor;
|
private readonly ClientRateLimitProcessor _processor;
|
||||||
|
|
||||||
public ClientRateLimitMiddleware(RequestDelegate next,
|
public ClientRateLimitMiddleware(OcelotRequestDelegate next,
|
||||||
IOcelotLoggerFactory loggerFactory,
|
IOcelotLoggerFactory loggerFactory,
|
||||||
IRequestScopedDataRepository requestScopedDataRepository,
|
|
||||||
IRateLimitCounterHandler counterHandler)
|
IRateLimitCounterHandler counterHandler)
|
||||||
: base(requestScopedDataRepository)
|
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_logger = loggerFactory.CreateLogger<ClientRateLimitMiddleware>();
|
_logger = loggerFactory.CreateLogger<ClientRateLimitMiddleware>();
|
||||||
@ -29,23 +28,23 @@ namespace Ocelot.RateLimit.Middleware
|
|||||||
_processor = new ClientRateLimitProcessor(counterHandler);
|
_processor = new ClientRateLimitProcessor(counterHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Invoke(HttpContext context)
|
public async Task Invoke(DownstreamContext context)
|
||||||
{
|
{
|
||||||
var options = DownstreamRoute.ReRoute.RateLimitOptions;
|
var options = context.DownstreamReRoute.RateLimitOptions;
|
||||||
// check if rate limiting is enabled
|
// check if rate limiting is enabled
|
||||||
if (!DownstreamRoute.ReRoute.EnableEndpointEndpointRateLimiting)
|
if (!context.DownstreamReRoute.EnableEndpointEndpointRateLimiting)
|
||||||
{
|
{
|
||||||
_logger.LogDebug($"EndpointRateLimiting is not enabled for {DownstreamRoute.ReRoute.DownstreamPathTemplate}");
|
_logger.LogDebug($"EndpointRateLimiting is not enabled for {context.DownstreamReRoute.DownstreamPathTemplate}");
|
||||||
await _next.Invoke(context);
|
await _next.Invoke(context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// compute identity from request
|
// compute identity from request
|
||||||
var identity = SetIdentity(context, options);
|
var identity = SetIdentity(context.HttpContext, options);
|
||||||
|
|
||||||
// check white list
|
// check white list
|
||||||
if (IsWhitelisted(identity, options))
|
if (IsWhitelisted(identity, options))
|
||||||
{
|
{
|
||||||
_logger.LogDebug($"{DownstreamRoute.ReRoute.DownstreamPathTemplate} is white listed from rate limiting");
|
_logger.LogDebug($"{context.DownstreamReRoute.DownstreamPathTemplate} is white listed from rate limiting");
|
||||||
await _next.Invoke(context);
|
await _next.Invoke(context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -63,11 +62,11 @@ namespace Ocelot.RateLimit.Middleware
|
|||||||
var retryAfter = _processor.RetryAfterFrom(counter.Timestamp, rule);
|
var retryAfter = _processor.RetryAfterFrom(counter.Timestamp, rule);
|
||||||
|
|
||||||
// log blocked request
|
// log blocked request
|
||||||
LogBlockedRequest(context, identity, counter, rule);
|
LogBlockedRequest(context.HttpContext, identity, counter, rule, context.DownstreamReRoute);
|
||||||
|
|
||||||
var retrystring = retryAfter.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
var retrystring = retryAfter.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||||
// break execution
|
// break execution
|
||||||
await ReturnQuotaExceededResponse(context, options, retrystring);
|
await ReturnQuotaExceededResponse(context.HttpContext, options, retrystring);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -76,8 +75,8 @@ namespace Ocelot.RateLimit.Middleware
|
|||||||
//set X-Rate-Limit headers for the longest period
|
//set X-Rate-Limit headers for the longest period
|
||||||
if (!options.DisableRateLimitHeaders)
|
if (!options.DisableRateLimitHeaders)
|
||||||
{
|
{
|
||||||
var headers = _processor.GetRateLimitHeaders(context, identity, options);
|
var headers = _processor.GetRateLimitHeaders(context.HttpContext, identity, options);
|
||||||
context.Response.OnStarting(SetRateLimitHeaders, state: headers);
|
context.HttpContext.Response.OnStarting(SetRateLimitHeaders, state: headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _next.Invoke(context);
|
await _next.Invoke(context);
|
||||||
@ -107,9 +106,9 @@ namespace Ocelot.RateLimit.Middleware
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule)
|
public virtual void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule, DownstreamReRoute downstreamReRoute)
|
||||||
{
|
{
|
||||||
_logger.LogDebug($"Request {identity.HttpVerb}:{identity.Path} from ClientId {identity.ClientId} has been blocked, quota {rule.Limit}/{rule.Period} exceeded by {counter.TotalRequests}. Blocked by rule { DownstreamRoute.ReRoute.UpstreamPathTemplate }, TraceIdentifier {httpContext.TraceIdentifier}.");
|
_logger.LogDebug($"Request {identity.HttpVerb}:{identity.Path} from ClientId {identity.ClientId} has been blocked, quota {rule.Limit}/{rule.Period} exceeded by {counter.TotalRequests}. Blocked by rule { downstreamReRoute.UpstreamPathTemplate }, TraceIdentifier {httpContext.TraceIdentifier}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual Task ReturnQuotaExceededResponse(HttpContext httpContext, RateLimitOptions option, string retryAfter)
|
public virtual Task ReturnQuotaExceededResponse(HttpContext httpContext, RateLimitOptions option, string retryAfter)
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Ocelot.Middleware.Pipeline;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Ocelot.RateLimit.Middleware
|
namespace Ocelot.RateLimit.Middleware
|
||||||
{
|
{
|
||||||
public static class RateLimitMiddlewareExtensions
|
public static class RateLimitMiddlewareExtensions
|
||||||
{
|
{
|
||||||
public static IApplicationBuilder UseRateLimiting(this IApplicationBuilder builder)
|
public static IOcelotPipelineBuilder UseRateLimiting(this IOcelotPipelineBuilder builder)
|
||||||
{
|
{
|
||||||
return builder.UseMiddleware<ClientRateLimitMiddleware>();
|
return builder.UseMiddleware<ClientRateLimitMiddleware>();
|
||||||
}
|
}
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
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,20 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,38 +2,36 @@ namespace Ocelot.Request.Middleware
|
|||||||
{
|
{
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||||
using Ocelot.Infrastructure.RequestData;
|
using Ocelot.Infrastructure.RequestData;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
|
|
||||||
public class DownstreamRequestInitialiserMiddleware : OcelotMiddleware
|
public class DownstreamRequestInitialiserMiddleware : OcelotMiddleware
|
||||||
{
|
{
|
||||||
private readonly RequestDelegate _next;
|
private readonly OcelotRequestDelegate _next;
|
||||||
private readonly IOcelotLogger _logger;
|
private readonly IOcelotLogger _logger;
|
||||||
private readonly Mapper.IRequestMapper _requestMapper;
|
private readonly Mapper.IRequestMapper _requestMapper;
|
||||||
|
|
||||||
public DownstreamRequestInitialiserMiddleware(RequestDelegate next,
|
public DownstreamRequestInitialiserMiddleware(OcelotRequestDelegate next,
|
||||||
IOcelotLoggerFactory loggerFactory,
|
IOcelotLoggerFactory loggerFactory,
|
||||||
IRequestScopedDataRepository requestScopedDataRepository,
|
|
||||||
Mapper.IRequestMapper requestMapper)
|
Mapper.IRequestMapper requestMapper)
|
||||||
:base(requestScopedDataRepository)
|
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_logger = loggerFactory.CreateLogger<DownstreamRequestInitialiserMiddleware>();
|
_logger = loggerFactory.CreateLogger<DownstreamRequestInitialiserMiddleware>();
|
||||||
_requestMapper = requestMapper;
|
_requestMapper = requestMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Invoke(HttpContext context)
|
public async Task Invoke(DownstreamContext context)
|
||||||
{
|
{
|
||||||
var downstreamRequest = await _requestMapper.Map(context.Request);
|
var downstreamRequest = await _requestMapper.Map(context.HttpContext.Request);
|
||||||
if (downstreamRequest.IsError)
|
if (downstreamRequest.IsError)
|
||||||
{
|
{
|
||||||
SetPipelineError(downstreamRequest.Errors);
|
SetPipelineError(context, downstreamRequest.Errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetDownstreamRequest(downstreamRequest.Data);
|
context.DownstreamRequest = downstreamRequest.Data;
|
||||||
|
|
||||||
await _next.Invoke(context);
|
await _next.Invoke(context);
|
||||||
}
|
}
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
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,15 +1,11 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Ocelot.Middleware.Pipeline;
|
||||||
|
|
||||||
namespace Ocelot.Request.Middleware
|
namespace Ocelot.Request.Middleware
|
||||||
{
|
{
|
||||||
public static class HttpRequestBuilderMiddlewareExtensions
|
public static class HttpRequestBuilderMiddlewareExtensions
|
||||||
{
|
{
|
||||||
public static IApplicationBuilder UseHttpRequestBuilderMiddleware(this IApplicationBuilder builder)
|
public static IOcelotPipelineBuilder UseDownstreamRequestInitialiser(this IOcelotPipelineBuilder builder)
|
||||||
{
|
|
||||||
return builder.UseMiddleware<HttpRequestBuilderMiddleware>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IApplicationBuilder UseDownstreamRequestInitialiser(this IApplicationBuilder builder)
|
|
||||||
{
|
{
|
||||||
return builder.UseMiddleware<DownstreamRequestInitialiserMiddleware>();
|
return builder.UseMiddleware<DownstreamRequestInitialiserMiddleware>();
|
||||||
}
|
}
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
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; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -8,63 +8,65 @@ using Ocelot.Middleware;
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||||
|
|
||||||
namespace Ocelot.RequestId.Middleware
|
namespace Ocelot.RequestId.Middleware
|
||||||
{
|
{
|
||||||
public class ReRouteRequestIdMiddleware : OcelotMiddleware
|
public class ReRouteRequestIdMiddleware : OcelotMiddleware
|
||||||
{
|
{
|
||||||
private readonly RequestDelegate _next;
|
private readonly OcelotRequestDelegate _next;
|
||||||
private readonly IOcelotLogger _logger;
|
private readonly IOcelotLogger _logger;
|
||||||
private readonly IRequestScopedDataRepository _requestScopedDataRepository;
|
private readonly IRequestScopedDataRepository _requestScopedDataRepository;
|
||||||
|
|
||||||
public ReRouteRequestIdMiddleware(RequestDelegate next,
|
|
||||||
|
public ReRouteRequestIdMiddleware(OcelotRequestDelegate next,
|
||||||
IOcelotLoggerFactory loggerFactory,
|
IOcelotLoggerFactory loggerFactory,
|
||||||
IRequestScopedDataRepository requestScopedDataRepository)
|
IRequestScopedDataRepository requestScopedDataRepository)
|
||||||
: base(requestScopedDataRepository)
|
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_logger = loggerFactory.CreateLogger<ReRouteRequestIdMiddleware>();
|
|
||||||
_requestScopedDataRepository = requestScopedDataRepository;
|
_requestScopedDataRepository = requestScopedDataRepository;
|
||||||
|
_logger = loggerFactory.CreateLogger<ReRouteRequestIdMiddleware>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Invoke(HttpContext context)
|
public async Task Invoke(DownstreamContext context)
|
||||||
{
|
{
|
||||||
SetOcelotRequestId(context);
|
SetOcelotRequestId(context);
|
||||||
await _next.Invoke(context);
|
await _next.Invoke(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetOcelotRequestId(HttpContext context)
|
private void SetOcelotRequestId(DownstreamContext context)
|
||||||
{
|
{
|
||||||
// if get request ID is set on upstream request then retrieve it
|
// if get request ID is set on upstream request then retrieve it
|
||||||
var key = DownstreamRoute.ReRoute.RequestIdKey ?? DefaultRequestIdKey.Value;
|
var key = context.DownstreamReRoute.RequestIdKey ?? DefaultRequestIdKey.Value;
|
||||||
|
|
||||||
StringValues upstreamRequestIds;
|
StringValues upstreamRequestIds;
|
||||||
if (context.Request.Headers.TryGetValue(key, out upstreamRequestIds))
|
if (context.HttpContext.Request.Headers.TryGetValue(key, out upstreamRequestIds))
|
||||||
{
|
{
|
||||||
//set the traceidentifier
|
//set the traceidentifier
|
||||||
context.TraceIdentifier = upstreamRequestIds.First();
|
context.HttpContext.TraceIdentifier = upstreamRequestIds.First();
|
||||||
|
|
||||||
//check if we have previous id
|
//todo fix looking in both places
|
||||||
|
//check if we have previous id in scoped repo
|
||||||
var previousRequestId = _requestScopedDataRepository.Get<string>("RequestId");
|
var previousRequestId = _requestScopedDataRepository.Get<string>("RequestId");
|
||||||
if(!previousRequestId.IsError && !string.IsNullOrEmpty(previousRequestId.Data))
|
if (!previousRequestId.IsError && !string.IsNullOrEmpty(previousRequestId.Data))
|
||||||
{
|
{
|
||||||
//we have a previous request id lets store it and update request id
|
//we have a previous request id lets store it and update request id
|
||||||
_requestScopedDataRepository.Add<string>("PreviousRequestId", previousRequestId.Data);
|
_requestScopedDataRepository.Add<string>("PreviousRequestId", previousRequestId.Data);
|
||||||
_requestScopedDataRepository.Update<string>("RequestId", context.TraceIdentifier);
|
_requestScopedDataRepository.Update<string>("RequestId", context.HttpContext.TraceIdentifier);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//else just add request id
|
//else just add request id
|
||||||
_requestScopedDataRepository.Add<string>("RequestId", context.TraceIdentifier);
|
_requestScopedDataRepository.Add<string>("RequestId", context.HttpContext.TraceIdentifier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set request ID on downstream request, if required
|
// set request ID on downstream request, if required
|
||||||
var requestId = new RequestId(DownstreamRoute?.ReRoute?.RequestIdKey, context.TraceIdentifier);
|
var requestId = new RequestId(context.DownstreamReRoute.RequestIdKey, context.HttpContext.TraceIdentifier);
|
||||||
|
|
||||||
if (ShouldAddRequestId(requestId, DownstreamRequest.Headers))
|
if (ShouldAddRequestId(requestId, context.DownstreamRequest.Headers))
|
||||||
{
|
{
|
||||||
AddRequestIdHeader(requestId, DownstreamRequest);
|
AddRequestIdHeader(requestId, context.DownstreamRequest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Ocelot.Middleware.Pipeline;
|
||||||
|
|
||||||
namespace Ocelot.RequestId.Middleware
|
namespace Ocelot.RequestId.Middleware
|
||||||
{
|
{
|
||||||
public static class RequestIdMiddlewareExtensions
|
public static class RequestIdMiddlewareExtensions
|
||||||
{
|
{
|
||||||
public static IApplicationBuilder UseRequestIdMiddleware(this IApplicationBuilder builder)
|
public static IOcelotPipelineBuilder UseRequestIdMiddleware(this IOcelotPipelineBuilder builder)
|
||||||
{
|
{
|
||||||
return builder.UseMiddleware<ReRouteRequestIdMiddleware>();
|
return builder.UseMiddleware<ReRouteRequestIdMiddleware>();
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Ocelot.Configuration;
|
||||||
using Ocelot.Errors;
|
using Ocelot.Errors;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
|
|
||||||
@ -17,7 +18,7 @@ namespace Ocelot.Requester
|
|||||||
_housed = new ConcurrentDictionary<string, IDelegatingHandlerHandlerProvider>();
|
_housed = new ConcurrentDictionary<string, IDelegatingHandlerHandlerProvider>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response<IDelegatingHandlerHandlerProvider> Get(Request.Request request)
|
public Response<IDelegatingHandlerHandlerProvider> Get(DownstreamReRoute request)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -28,7 +29,15 @@ namespace Ocelot.Requester
|
|||||||
return new OkResponse<IDelegatingHandlerHandlerProvider>(provider);
|
return new OkResponse<IDelegatingHandlerHandlerProvider>(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
provider = _factory.Get(request);
|
//todo - unit test for this
|
||||||
|
var providerResponse = _factory.Get(request);
|
||||||
|
|
||||||
|
if (providerResponse.IsError)
|
||||||
|
{
|
||||||
|
return new ErrorResponse<IDelegatingHandlerHandlerProvider>(providerResponse.Errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
provider = providerResponse.Data;
|
||||||
AddHoused(request.ReRouteKey, provider);
|
AddHoused(request.ReRouteKey, provider);
|
||||||
return new OkResponse<IDelegatingHandlerHandlerProvider>(provider);
|
return new OkResponse<IDelegatingHandlerHandlerProvider>(provider);
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,31 @@
|
|||||||
|
using System;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using Ocelot.Configuration;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
|
using Ocelot.Requester.QoS;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
|
||||||
namespace Ocelot.Requester
|
namespace Ocelot.Requester
|
||||||
{
|
{
|
||||||
public class DelegatingHandlerHandlerProviderFactory : IDelegatingHandlerHandlerProviderFactory
|
public class DelegatingHandlerHandlerProviderFactory : IDelegatingHandlerHandlerProviderFactory
|
||||||
{
|
{
|
||||||
private readonly ITracingHandler _tracingHandler;
|
private readonly ITracingHandlerFactory _factory;
|
||||||
private readonly IOcelotLoggerFactory _loggerFactory;
|
private readonly IOcelotLoggerFactory _loggerFactory;
|
||||||
private readonly IDelegatingHandlerHandlerProvider _allRoutesProvider;
|
private readonly IDelegatingHandlerHandlerProvider _allRoutesProvider;
|
||||||
|
private readonly IQosProviderHouse _qosProviderHouse;
|
||||||
|
|
||||||
public DelegatingHandlerHandlerProviderFactory(IOcelotLoggerFactory loggerFactory, IDelegatingHandlerHandlerProvider allRoutesProvider, ITracingHandler tracingHandler)
|
public DelegatingHandlerHandlerProviderFactory(IOcelotLoggerFactory loggerFactory,
|
||||||
|
IDelegatingHandlerHandlerProvider allRoutesProvider,
|
||||||
|
ITracingHandlerFactory factory,
|
||||||
|
IQosProviderHouse qosProviderHouse)
|
||||||
{
|
{
|
||||||
_tracingHandler = tracingHandler;
|
_factory = factory;
|
||||||
_loggerFactory = loggerFactory;
|
_loggerFactory = loggerFactory;
|
||||||
_allRoutesProvider = allRoutesProvider;
|
_allRoutesProvider = allRoutesProvider;
|
||||||
|
_qosProviderHouse = qosProviderHouse;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDelegatingHandlerHandlerProvider Get(Request.Request request)
|
public Response<IDelegatingHandlerHandlerProvider> Get(DownstreamReRoute request)
|
||||||
{
|
{
|
||||||
var handlersAppliedToAll = _allRoutesProvider.Get();
|
var handlersAppliedToAll = _allRoutesProvider.Get();
|
||||||
|
|
||||||
@ -27,17 +36,24 @@ namespace Ocelot.Requester
|
|||||||
provider.Add(handler);
|
provider.Add(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.IsTracing)
|
if (request.HttpHandlerOptions.UseTracing)
|
||||||
{
|
{
|
||||||
provider.Add(() => (DelegatingHandler)_tracingHandler);
|
provider.Add(() => (DelegatingHandler)_factory.Get());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.IsQos)
|
if (request.IsQos)
|
||||||
{
|
{
|
||||||
provider.Add(() => new PollyCircuitBreakingDelegatingHandler(request.QosProvider, _loggerFactory));
|
var qosProvider = _qosProviderHouse.Get(request);
|
||||||
|
|
||||||
|
if (qosProvider.IsError)
|
||||||
|
{
|
||||||
|
return new ErrorResponse<IDelegatingHandlerHandlerProvider>(qosProvider.Errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
provider.Add(() => new PollyCircuitBreakingDelegatingHandler(qosProvider.Data, _loggerFactory));
|
||||||
}
|
}
|
||||||
|
|
||||||
return provider;
|
return new OkResponse<IDelegatingHandlerHandlerProvider>(provider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using Ocelot.Configuration;
|
||||||
|
|
||||||
namespace Ocelot.Requester
|
namespace Ocelot.Requester
|
||||||
{
|
{
|
||||||
@ -12,21 +13,23 @@ namespace Ocelot.Requester
|
|||||||
_house = house;
|
_house = house;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IHttpClient Create(Request.Request request)
|
public IHttpClient Create(DownstreamReRoute request)
|
||||||
{
|
{
|
||||||
var httpclientHandler = new HttpClientHandler { AllowAutoRedirect = request.AllowAutoRedirect, UseCookies = request.UseCookieContainer};
|
var httpclientHandler = new HttpClientHandler { AllowAutoRedirect = request.HttpHandlerOptions.AllowAutoRedirect, UseCookies = request.HttpHandlerOptions.UseCookieContainer};
|
||||||
|
|
||||||
var client = new HttpClient(CreateHttpMessageHandler(httpclientHandler, request));
|
var client = new HttpClient(CreateHttpMessageHandler(httpclientHandler, request));
|
||||||
|
|
||||||
return new HttpClientWrapper(client);
|
return new HttpClientWrapper(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
private HttpMessageHandler CreateHttpMessageHandler(HttpMessageHandler httpMessageHandler, Request.Request request)
|
private HttpMessageHandler CreateHttpMessageHandler(HttpMessageHandler httpMessageHandler, DownstreamReRoute request)
|
||||||
{
|
{
|
||||||
var provider = _house.Get(request);
|
var provider = _house.Get(request);
|
||||||
|
|
||||||
|
var handlers = provider.Data.Get();
|
||||||
|
|
||||||
//todo handle error
|
//todo handle error
|
||||||
provider.Data.Get()
|
handlers
|
||||||
.Select(handler => handler)
|
.Select(handler => handler)
|
||||||
.Reverse()
|
.Reverse()
|
||||||
.ToList()
|
.ToList()
|
||||||
|
@ -2,6 +2,7 @@ using System;
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
|
using Ocelot.Middleware;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using Polly.CircuitBreaker;
|
using Polly.CircuitBreaker;
|
||||||
using Polly.Timeout;
|
using Polly.Timeout;
|
||||||
@ -23,7 +24,7 @@ namespace Ocelot.Requester
|
|||||||
_house = house;
|
_house = house;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Response<HttpResponseMessage>> GetResponse(Request.Request request)
|
public async Task<Response<HttpResponseMessage>> GetResponse(DownstreamContext request)
|
||||||
{
|
{
|
||||||
var builder = new HttpClientBuilder(_house);
|
var builder = new HttpClientBuilder(_house);
|
||||||
|
|
||||||
@ -33,7 +34,7 @@ namespace Ocelot.Requester
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var response = await httpClient.SendAsync(request.HttpRequestMessage);
|
var response = await httpClient.SendAsync(request.DownstreamRequest);
|
||||||
return new OkResponse<HttpResponseMessage>(response);
|
return new OkResponse<HttpResponseMessage>(response);
|
||||||
}
|
}
|
||||||
catch (TimeoutRejectedException exception)
|
catch (TimeoutRejectedException exception)
|
||||||
@ -57,26 +58,21 @@ namespace Ocelot.Requester
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IHttpClient GetHttpClient(string cacheKey, IHttpClientBuilder builder, Request.Request request)
|
private IHttpClient GetHttpClient(string cacheKey, IHttpClientBuilder builder, DownstreamContext request)
|
||||||
{
|
{
|
||||||
var httpClient = _cacheHandlers.Get(cacheKey);
|
var httpClient = _cacheHandlers.Get(cacheKey);
|
||||||
|
|
||||||
if (httpClient == null)
|
if (httpClient == null)
|
||||||
{
|
{
|
||||||
httpClient = builder.Create(request);
|
httpClient = builder.Create(request.DownstreamReRoute);
|
||||||
}
|
}
|
||||||
|
|
||||||
return httpClient;
|
return httpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetCacheKey(Request.Request request)
|
private string GetCacheKey(DownstreamContext request)
|
||||||
{
|
{
|
||||||
var baseUrl = $"{request.HttpRequestMessage.RequestUri.Scheme}://{request.HttpRequestMessage.RequestUri.Authority}";
|
var baseUrl = $"{request.DownstreamRequest.RequestUri.Scheme}://{request.DownstreamRequest.RequestUri.Authority}";
|
||||||
|
|
||||||
if (request.IsQos)
|
|
||||||
{
|
|
||||||
baseUrl = $"{baseUrl}{request.QosProvider.CircuitBreaker.CircuitBreakerPolicy.PolicyKey}";
|
|
||||||
}
|
|
||||||
|
|
||||||
return baseUrl;
|
return baseUrl;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
|
using Ocelot.Configuration;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
|
|
||||||
namespace Ocelot.Requester
|
namespace Ocelot.Requester
|
||||||
{
|
{
|
||||||
public interface IDelegatingHandlerHandlerHouse
|
public interface IDelegatingHandlerHandlerHouse
|
||||||
{
|
{
|
||||||
Response<IDelegatingHandlerHandlerProvider> Get(Request.Request request);
|
Response<IDelegatingHandlerHandlerProvider> Get(DownstreamReRoute request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
|
using Ocelot.Configuration;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
|
||||||
namespace Ocelot.Requester
|
namespace Ocelot.Requester
|
||||||
{
|
{
|
||||||
public interface IDelegatingHandlerHandlerProviderFactory
|
public interface IDelegatingHandlerHandlerProviderFactory
|
||||||
{
|
{
|
||||||
IDelegatingHandlerHandlerProvider Get(Request.Request request);
|
Response<IDelegatingHandlerHandlerProvider> Get(DownstreamReRoute request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,6 @@ namespace Ocelot.Requester
|
|||||||
/// Creates the <see cref="HttpClient"/>
|
/// Creates the <see cref="HttpClient"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="request"></param>
|
/// <param name="request"></param>
|
||||||
IHttpClient Create(Request.Request request);
|
IHttpClient Create(DownstreamReRoute request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Ocelot.Middleware;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
|
|
||||||
namespace Ocelot.Requester
|
namespace Ocelot.Requester
|
||||||
{
|
{
|
||||||
public interface IHttpRequester
|
public interface IHttpRequester
|
||||||
{
|
{
|
||||||
Task<Response<HttpResponseMessage>> GetResponse(Request.Request request);
|
Task<Response<HttpResponseMessage>> GetResponse(DownstreamContext request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
7
src/Ocelot/Requester/ITracingHandlerFactory.cs
Normal file
7
src/Ocelot/Requester/ITracingHandlerFactory.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace Ocelot.Requester
|
||||||
|
{
|
||||||
|
public interface ITracingHandlerFactory
|
||||||
|
{
|
||||||
|
ITracingHandler Get();
|
||||||
|
}
|
||||||
|
}
|
@ -3,42 +3,41 @@ using Ocelot.Infrastructure.RequestData;
|
|||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||||
|
using Ocelot.Requester.QoS;
|
||||||
|
|
||||||
namespace Ocelot.Requester.Middleware
|
namespace Ocelot.Requester.Middleware
|
||||||
{
|
{
|
||||||
public class HttpRequesterMiddleware : OcelotMiddleware
|
public class HttpRequesterMiddleware : OcelotMiddleware
|
||||||
{
|
{
|
||||||
private readonly RequestDelegate _next;
|
private readonly OcelotRequestDelegate _next;
|
||||||
private readonly IHttpRequester _requester;
|
private readonly IHttpRequester _requester;
|
||||||
private readonly IOcelotLogger _logger;
|
private readonly IOcelotLogger _logger;
|
||||||
|
|
||||||
public HttpRequesterMiddleware(RequestDelegate next,
|
public HttpRequesterMiddleware(OcelotRequestDelegate next,
|
||||||
IOcelotLoggerFactory loggerFactory,
|
IOcelotLoggerFactory loggerFactory,
|
||||||
IHttpRequester requester,
|
IHttpRequester requester)
|
||||||
IRequestScopedDataRepository requestScopedDataRepository
|
|
||||||
)
|
|
||||||
:base(requestScopedDataRepository)
|
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_requester = requester;
|
_requester = requester;
|
||||||
_logger = loggerFactory.CreateLogger<HttpRequesterMiddleware>();
|
_logger = loggerFactory.CreateLogger<HttpRequesterMiddleware>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Invoke(HttpContext context)
|
public async Task Invoke(DownstreamContext context)
|
||||||
{
|
{
|
||||||
var response = await _requester.GetResponse(Request);
|
var response = await _requester.GetResponse(context);
|
||||||
|
|
||||||
if (response.IsError)
|
if (response.IsError)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("IHttpRequester returned an error, setting pipeline error");
|
_logger.LogDebug("IHttpRequester returned an error, setting pipeline error");
|
||||||
|
|
||||||
SetPipelineError(response.Errors);
|
SetPipelineError(context, response.Errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogDebug("setting http response message");
|
_logger.LogDebug("setting http response message");
|
||||||
|
|
||||||
SetHttpResponseMessageThisRequest(response.Data);
|
context.DownstreamResponse = response.Data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Ocelot.Middleware.Pipeline;
|
||||||
|
|
||||||
namespace Ocelot.Requester.Middleware
|
namespace Ocelot.Requester.Middleware
|
||||||
{
|
{
|
||||||
public static class HttpRequesterMiddlewareExtensions
|
public static class HttpRequesterMiddlewareExtensions
|
||||||
{
|
{
|
||||||
public static IApplicationBuilder UseHttpRequesterMiddleware(this IApplicationBuilder builder)
|
public static IOcelotPipelineBuilder UseHttpRequesterMiddleware(this IOcelotPipelineBuilder builder)
|
||||||
{
|
{
|
||||||
return builder.UseMiddleware<HttpRequesterMiddleware>();
|
return builder.UseMiddleware<HttpRequesterMiddleware>();
|
||||||
}
|
}
|
||||||
|
@ -12,11 +12,6 @@ namespace Ocelot.Requester
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public class NoTracingHandler : DelegatingHandler, ITracingHandler
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class OcelotHttpTracingHandler : DelegatingHandler, ITracingHandler
|
public class OcelotHttpTracingHandler : DelegatingHandler, ITracingHandler
|
||||||
{
|
{
|
||||||
private readonly IServiceTracer _tracer;
|
private readonly IServiceTracer _tracer;
|
||||||
|
@ -5,6 +5,6 @@ namespace Ocelot.Requester.QoS
|
|||||||
{
|
{
|
||||||
public interface IQoSProviderFactory
|
public interface IQoSProviderFactory
|
||||||
{
|
{
|
||||||
IQoSProvider Get(ReRoute reRoute);
|
IQoSProvider Get(DownstreamReRoute reRoute);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,6 @@ namespace Ocelot.Requester.QoS
|
|||||||
{
|
{
|
||||||
public interface IQosProviderHouse
|
public interface IQosProviderHouse
|
||||||
{
|
{
|
||||||
Response<IQoSProvider> Get(ReRoute reRoute);
|
Response<IQoSProvider> Get(DownstreamReRoute reRoute);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,7 +15,7 @@ namespace Ocelot.Requester.QoS
|
|||||||
private readonly IOcelotLogger _logger;
|
private readonly IOcelotLogger _logger;
|
||||||
private readonly CircuitBreaker _circuitBreaker;
|
private readonly CircuitBreaker _circuitBreaker;
|
||||||
|
|
||||||
public PollyQoSProvider(ReRoute reRoute, IOcelotLoggerFactory loggerFactory)
|
public PollyQoSProvider(DownstreamReRoute reRoute, IOcelotLoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
_logger = loggerFactory.CreateLogger<PollyQoSProvider>();
|
_logger = loggerFactory.CreateLogger<PollyQoSProvider>();
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ namespace Ocelot.Requester.QoS
|
|||||||
_loggerFactory = loggerFactory;
|
_loggerFactory = loggerFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IQoSProvider Get(ReRoute reRoute)
|
public IQoSProvider Get(DownstreamReRoute reRoute)
|
||||||
{
|
{
|
||||||
if (reRoute.IsQos)
|
if (reRoute.IsQos)
|
||||||
{
|
{
|
||||||
|
@ -17,7 +17,7 @@ namespace Ocelot.Requester.QoS
|
|||||||
_qoSProviders = new ConcurrentDictionary<string, IQoSProvider>();
|
_qoSProviders = new ConcurrentDictionary<string, IQoSProvider>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response<IQoSProvider> Get(ReRoute reRoute)
|
public Response<IQoSProvider> Get(DownstreamReRoute reRoute)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
32
src/Ocelot/Requester/TracingHandlerFactory.cs
Normal file
32
src/Ocelot/Requester/TracingHandlerFactory.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
using Butterfly.Client.Tracing;
|
||||||
|
using Butterfly.OpenTracing;
|
||||||
|
|
||||||
|
namespace Ocelot.Requester
|
||||||
|
{
|
||||||
|
public class TracingHandlerFactory : ITracingHandlerFactory
|
||||||
|
{
|
||||||
|
private readonly IServiceTracer _tracer;
|
||||||
|
|
||||||
|
public TracingHandlerFactory(IServiceTracer tracer)
|
||||||
|
{
|
||||||
|
_tracer = tracer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITracingHandler Get()
|
||||||
|
{
|
||||||
|
return new OcelotHttpTracingHandler(_tracer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FakeServiceTracer : IServiceTracer
|
||||||
|
{
|
||||||
|
public ITracer Tracer { get; }
|
||||||
|
public string ServiceName { get; }
|
||||||
|
public string Environment { get; }
|
||||||
|
public string Identity { get; }
|
||||||
|
public ISpan Start(ISpanBuilder spanBuilder)
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ using Ocelot.Logging;
|
|||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||||
|
|
||||||
namespace Ocelot.Responder.Middleware
|
namespace Ocelot.Responder.Middleware
|
||||||
{
|
{
|
||||||
@ -13,18 +14,16 @@ namespace Ocelot.Responder.Middleware
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ResponderMiddleware : OcelotMiddleware
|
public class ResponderMiddleware : OcelotMiddleware
|
||||||
{
|
{
|
||||||
private readonly RequestDelegate _next;
|
private readonly OcelotRequestDelegate _next;
|
||||||
private readonly IHttpResponder _responder;
|
private readonly IHttpResponder _responder;
|
||||||
private readonly IErrorsToHttpStatusCodeMapper _codeMapper;
|
private readonly IErrorsToHttpStatusCodeMapper _codeMapper;
|
||||||
private readonly IOcelotLogger _logger;
|
private readonly IOcelotLogger _logger;
|
||||||
|
|
||||||
public ResponderMiddleware(RequestDelegate next,
|
public ResponderMiddleware(OcelotRequestDelegate next,
|
||||||
IHttpResponder responder,
|
IHttpResponder responder,
|
||||||
IOcelotLoggerFactory loggerFactory,
|
IOcelotLoggerFactory loggerFactory,
|
||||||
IRequestScopedDataRepository requestScopedDataRepository,
|
|
||||||
IErrorsToHttpStatusCodeMapper codeMapper
|
IErrorsToHttpStatusCodeMapper codeMapper
|
||||||
)
|
)
|
||||||
:base(requestScopedDataRepository)
|
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_responder = responder;
|
_responder = responder;
|
||||||
@ -33,20 +32,20 @@ namespace Ocelot.Responder.Middleware
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Invoke(HttpContext context)
|
public async Task Invoke(DownstreamContext context)
|
||||||
{
|
{
|
||||||
await _next.Invoke(context);
|
await _next.Invoke(context);
|
||||||
|
|
||||||
if (PipelineError)
|
if (context.IsError)
|
||||||
{
|
{
|
||||||
var errors = PipelineErrors;
|
var errors = context.Errors;
|
||||||
_logger.LogError($"{PipelineErrors.Count} pipeline errors found in {MiddlewareName}. Setting error response status code");
|
_logger.LogError($"{errors.Count} pipeline errors found in {MiddlewareName}. Setting error response status code");
|
||||||
SetErrorResponse(context, errors);
|
SetErrorResponse(context.HttpContext, errors);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.LogDebug("no pipeline errors, setting and returning completed response");
|
_logger.LogDebug("no pipeline errors, setting and returning completed response");
|
||||||
await _responder.SetResponseOnHttpContext(context, HttpResponseMessage);
|
await _responder.SetResponseOnHttpContext(context.HttpContext, context.DownstreamResponse);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Ocelot.Middleware.Pipeline;
|
||||||
|
|
||||||
namespace Ocelot.Responder.Middleware
|
namespace Ocelot.Responder.Middleware
|
||||||
{
|
{
|
||||||
public static class ResponderMiddlewareExtensions
|
public static class ResponderMiddlewareExtensions
|
||||||
{
|
{
|
||||||
public static IApplicationBuilder UseResponderMiddleware(this IApplicationBuilder builder)
|
public static IOcelotPipelineBuilder UseResponderMiddleware(this IOcelotPipelineBuilder builder)
|
||||||
{
|
{
|
||||||
return builder.UseMiddleware<ResponderMiddleware>();
|
return builder.UseMiddleware<ResponderMiddleware>();
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,6 @@ namespace Ocelot.ServiceDiscovery
|
|||||||
{
|
{
|
||||||
public interface IServiceDiscoveryProviderFactory
|
public interface IServiceDiscoveryProviderFactory
|
||||||
{
|
{
|
||||||
IServiceDiscoveryProvider Get(ServiceProviderConfiguration serviceConfig, ReRoute reRoute);
|
IServiceDiscoveryProvider Get(ServiceProviderConfiguration serviceConfig, DownstreamReRoute reRoute);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -14,7 +14,7 @@ namespace Ocelot.ServiceDiscovery
|
|||||||
_factory = factory;
|
_factory = factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IServiceDiscoveryProvider Get(ServiceProviderConfiguration serviceConfig, ReRoute reRoute)
|
public IServiceDiscoveryProvider Get(ServiceProviderConfiguration serviceConfig, DownstreamReRoute reRoute)
|
||||||
{
|
{
|
||||||
if (reRoute.UseServiceDiscovery)
|
if (reRoute.UseServiceDiscovery)
|
||||||
{
|
{
|
||||||
|
372
test/Ocelot.AcceptanceTests/AggregateTests.cs
Normal file
372
test/Ocelot.AcceptanceTests/AggregateTests.cs
Normal file
@ -0,0 +1,372 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
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 AggregateTests : IDisposable
|
||||||
|
{
|
||||||
|
private IWebHost _serviceOneBuilder;
|
||||||
|
private IWebHost _serviceTwoBuilder;
|
||||||
|
private readonly Steps _steps;
|
||||||
|
private string _downstreamPathOne;
|
||||||
|
private string _downstreamPathTwo;
|
||||||
|
|
||||||
|
public AggregateTests()
|
||||||
|
{
|
||||||
|
_steps = new Steps();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_response_200_with_simple_url()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 51885,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpstreamPathTemplate = "/laura",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
Key = "Laura"
|
||||||
|
},
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 51886,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpstreamPathTemplate = "/tom",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
Key = "Tom"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Aggregates = new List<FileAggregateReRoute>
|
||||||
|
{
|
||||||
|
new FileAggregateReRoute
|
||||||
|
{
|
||||||
|
UpstreamPathTemplate = "/",
|
||||||
|
UpstreamHost = "localhost",
|
||||||
|
ReRouteKeys = new List<string>
|
||||||
|
{
|
||||||
|
"Tom",
|
||||||
|
"Laura"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var expected = "{\"Laura\":{Hello from Laura},\"Tom\":{Hello from Tom}}";
|
||||||
|
|
||||||
|
this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51885", "/", 200, "{Hello from Laura}"))
|
||||||
|
.Given(x => x.GivenServiceTwoIsRunning("http://localhost:51886", "/", 200, "{Hello from Tom}"))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe(expected))
|
||||||
|
.And(x => ThenTheDownstreamUrlPathShouldBe("/", "/"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_response_200_with_simple_url_one_service_404()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 51881,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpstreamPathTemplate = "/laura",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
Key = "Laura"
|
||||||
|
},
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 51882,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpstreamPathTemplate = "/tom",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
Key = "Tom"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Aggregates = new List<FileAggregateReRoute>
|
||||||
|
{
|
||||||
|
new FileAggregateReRoute
|
||||||
|
{
|
||||||
|
UpstreamPathTemplate = "/",
|
||||||
|
UpstreamHost = "localhost",
|
||||||
|
ReRouteKeys = new List<string>
|
||||||
|
{
|
||||||
|
"Tom",
|
||||||
|
"Laura"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var expected = "{\"Laura\":,\"Tom\":{Hello from Tom}}";
|
||||||
|
|
||||||
|
this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51881", "/", 404, ""))
|
||||||
|
.Given(x => x.GivenServiceTwoIsRunning("http://localhost:51882", "/", 200, "{Hello from Tom}"))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe(expected))
|
||||||
|
.And(x => ThenTheDownstreamUrlPathShouldBe("/", "/"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_response_200_with_simple_url_both_service_404()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 51883,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpstreamPathTemplate = "/laura",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
Key = "Laura"
|
||||||
|
},
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 51884,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpstreamPathTemplate = "/tom",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
Key = "Tom"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Aggregates = new List<FileAggregateReRoute>
|
||||||
|
{
|
||||||
|
new FileAggregateReRoute
|
||||||
|
{
|
||||||
|
UpstreamPathTemplate = "/",
|
||||||
|
UpstreamHost = "localhost",
|
||||||
|
ReRouteKeys = new List<string>
|
||||||
|
{
|
||||||
|
"Tom",
|
||||||
|
"Laura"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var expected = "{\"Laura\":,\"Tom\":}";
|
||||||
|
|
||||||
|
this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51883", "/", 404, ""))
|
||||||
|
.Given(x => x.GivenServiceTwoIsRunning("http://localhost:51884", "/", 404, ""))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe(expected))
|
||||||
|
.And(x => ThenTheDownstreamUrlPathShouldBe("/", "/"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_be_thread_safe()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 51878,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpstreamPathTemplate = "/laura",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
Key = "Laura"
|
||||||
|
},
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 51880,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpstreamPathTemplate = "/tom",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
Key = "Tom"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Aggregates = new List<FileAggregateReRoute>
|
||||||
|
{
|
||||||
|
new FileAggregateReRoute
|
||||||
|
{
|
||||||
|
UpstreamPathTemplate = "/",
|
||||||
|
UpstreamHost = "localhost",
|
||||||
|
ReRouteKeys = new List<string>
|
||||||
|
{
|
||||||
|
"Tom",
|
||||||
|
"Laura"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51878", "/", 200, "{Hello from Laura}"))
|
||||||
|
.Given(x => x.GivenServiceTwoIsRunning("http://localhost:51880", "/", 200, "{Hello from Tom}"))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
|
.When(x => _steps.WhenIMakeLotsOfDifferentRequestsToTheApiGateway())
|
||||||
|
.And(x => ThenTheDownstreamUrlPathShouldBe("/", "/"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenServiceOneIsRunning(string baseUrl, string basePath, int statusCode, string responseBody)
|
||||||
|
{
|
||||||
|
_serviceOneBuilder = new WebHostBuilder()
|
||||||
|
.UseUrls(baseUrl)
|
||||||
|
.UseKestrel()
|
||||||
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
|
.UseIISIntegration()
|
||||||
|
.Configure(app =>
|
||||||
|
{
|
||||||
|
app.UsePathBase(basePath);
|
||||||
|
app.Run(async context =>
|
||||||
|
{
|
||||||
|
_downstreamPathOne = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
|
||||||
|
|
||||||
|
if(_downstreamPathOne != 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();
|
||||||
|
|
||||||
|
_serviceOneBuilder.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenServiceTwoIsRunning(string baseUrl, string basePath, int statusCode, string responseBody)
|
||||||
|
{
|
||||||
|
_serviceOneBuilder = new WebHostBuilder()
|
||||||
|
.UseUrls(baseUrl)
|
||||||
|
.UseKestrel()
|
||||||
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
|
.UseIISIntegration()
|
||||||
|
.Configure(app =>
|
||||||
|
{
|
||||||
|
app.UsePathBase(basePath);
|
||||||
|
app.Run(async context =>
|
||||||
|
{
|
||||||
|
_downstreamPathTwo = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
|
||||||
|
|
||||||
|
if(_downstreamPathTwo != 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();
|
||||||
|
|
||||||
|
_serviceOneBuilder.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void ThenTheDownstreamUrlPathShouldBe(string expectedDownstreamPathOne, string expectedDownstreamPath)
|
||||||
|
{
|
||||||
|
_downstreamPathOne.ShouldBe(expectedDownstreamPathOne);
|
||||||
|
_downstreamPathTwo.ShouldBe(expectedDownstreamPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_serviceOneBuilder?.Dispose();
|
||||||
|
_serviceTwoBuilder?.Dispose();
|
||||||
|
_steps.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
225
test/Ocelot.AcceptanceTests/ButterflyTracingTests.cs
Normal file
225
test/Ocelot.AcceptanceTests/ButterflyTracingTests.cs
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
|
using Shouldly;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
using Butterfly.Client.AspNetCore;
|
||||||
|
using static Rafty.Infrastructure.Wait;
|
||||||
|
|
||||||
|
namespace Ocelot.AcceptanceTests
|
||||||
|
{
|
||||||
|
public class ButterflyTracingTests : IDisposable
|
||||||
|
{
|
||||||
|
private IWebHost _serviceOneBuilder;
|
||||||
|
private IWebHost _serviceTwoBuilder;
|
||||||
|
private IWebHost _fakeButterfly;
|
||||||
|
private readonly Steps _steps;
|
||||||
|
private string _downstreamPathOne;
|
||||||
|
private string _downstreamPathTwo;
|
||||||
|
private int _butterflyCalled;
|
||||||
|
|
||||||
|
public ButterflyTracingTests()
|
||||||
|
{
|
||||||
|
_steps = new Steps();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_forward_tracing_information_from_ocelot_and_downstream_services()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/api/values",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 51887,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpstreamPathTemplate = "/api001/values",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
HttpHandlerOptions = new FileHttpHandlerOptions
|
||||||
|
{
|
||||||
|
UseTracing = true
|
||||||
|
},
|
||||||
|
QoSOptions = new FileQoSOptions
|
||||||
|
{
|
||||||
|
ExceptionsAllowedBeforeBreaking = 3,
|
||||||
|
DurationOfBreak = 10,
|
||||||
|
TimeoutValue = 5000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/api/values",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 51888,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpstreamPathTemplate = "/api002/values",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
HttpHandlerOptions = new FileHttpHandlerOptions
|
||||||
|
{
|
||||||
|
UseTracing = true
|
||||||
|
},
|
||||||
|
QoSOptions = new FileQoSOptions
|
||||||
|
{
|
||||||
|
ExceptionsAllowedBeforeBreaking = 3,
|
||||||
|
DurationOfBreak = 10,
|
||||||
|
TimeoutValue = 5000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var butterflyUrl = "http://localhost:9618";
|
||||||
|
|
||||||
|
this.Given(x => GivenServiceOneIsRunning("http://localhost:51887", "/api/values", 200, "Hello from Laura", butterflyUrl))
|
||||||
|
.And(x => GivenServiceTwoIsRunning("http://localhost:51888", "/api/values", 200, "Hello from Tom", butterflyUrl))
|
||||||
|
.And(x => GivenFakeButterfly(butterflyUrl))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunningUsingButterfly(butterflyUrl))
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/api001/values"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/api002/values"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Tom"))
|
||||||
|
.BDDfy();
|
||||||
|
|
||||||
|
|
||||||
|
var commandOnAllStateMachines = WaitFor(5000).Until(() => _butterflyCalled == 4);
|
||||||
|
|
||||||
|
commandOnAllStateMachines.ShouldBeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenServiceOneIsRunning(string baseUrl, string basePath, int statusCode, string responseBody, string butterflyUrl)
|
||||||
|
{
|
||||||
|
_serviceOneBuilder = new WebHostBuilder()
|
||||||
|
.UseUrls(baseUrl)
|
||||||
|
.UseKestrel()
|
||||||
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
|
.UseIISIntegration()
|
||||||
|
.ConfigureServices(services => {
|
||||||
|
services.AddButterfly(option =>
|
||||||
|
{
|
||||||
|
option.CollectorUrl = butterflyUrl;
|
||||||
|
option.Service = "Service One";
|
||||||
|
option.IgnoredRoutesRegexPatterns = new string[0];
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.Configure(app =>
|
||||||
|
{
|
||||||
|
app.UsePathBase(basePath);
|
||||||
|
app.Run(async context =>
|
||||||
|
{
|
||||||
|
_downstreamPathOne = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
|
||||||
|
|
||||||
|
if(_downstreamPathOne != 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();
|
||||||
|
|
||||||
|
_serviceOneBuilder.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenFakeButterfly(string baseUrl)
|
||||||
|
{
|
||||||
|
_fakeButterfly = new WebHostBuilder()
|
||||||
|
.UseUrls(baseUrl)
|
||||||
|
.UseKestrel()
|
||||||
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
|
.UseIISIntegration()
|
||||||
|
.Configure(app =>
|
||||||
|
{
|
||||||
|
app.Run(async context =>
|
||||||
|
{
|
||||||
|
_butterflyCalled++;
|
||||||
|
await context.Response.WriteAsync("OK...");
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_fakeButterfly.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenServiceTwoIsRunning(string baseUrl, string basePath, int statusCode, string responseBody, string butterflyUrl)
|
||||||
|
{
|
||||||
|
_serviceTwoBuilder = new WebHostBuilder()
|
||||||
|
.UseUrls(baseUrl)
|
||||||
|
.UseKestrel()
|
||||||
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
|
.UseIISIntegration()
|
||||||
|
.ConfigureServices(services => {
|
||||||
|
services.AddButterfly(option =>
|
||||||
|
{
|
||||||
|
option.CollectorUrl = butterflyUrl;
|
||||||
|
option.Service = "Service Two";
|
||||||
|
option.IgnoredRoutesRegexPatterns = new string[0];
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.Configure(app =>
|
||||||
|
{
|
||||||
|
app.UsePathBase(basePath);
|
||||||
|
app.Run(async context =>
|
||||||
|
{
|
||||||
|
_downstreamPathTwo = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
|
||||||
|
|
||||||
|
if(_downstreamPathTwo != 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();
|
||||||
|
|
||||||
|
_serviceTwoBuilder.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void ThenTheDownstreamUrlPathShouldBe(string expectedDownstreamPathOne, string expectedDownstreamPath)
|
||||||
|
{
|
||||||
|
_downstreamPathOne.ShouldBe(expectedDownstreamPathOne);
|
||||||
|
_downstreamPathTwo.ShouldBe(expectedDownstreamPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_serviceOneBuilder?.Dispose();
|
||||||
|
_serviceTwoBuilder?.Dispose();
|
||||||
|
_fakeButterfly?.Dispose();
|
||||||
|
_steps.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user