mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 06:22:50 +08:00
Feature/downstream aggregation (#248)
* started messing around with this on the train last night * mega hacking away to change middleware into Ocelot iddleware * scoped data repo back in * broken commit getting tests working * another broken commit farting around with tests * all unit tests passing again * mw pipeline for ocelot...still loads of hacks but getting there now to get acceptance tests working, then fix config so you can have aggregate and then imlement multiplexer, then mapping to response...loads to do * all tests passing before aggregation feature implemented * removed all the request middleware stuff we dont need it * updated how errors work...tho i think there could be edge case here when aggregating because one downstream could error and this would effect another * removed multiplexer so you dont have to send route down, this isnt very thread safe...sigh * hacking around getting the config for aggregates in, this might change * refactored builder and unit tests passing now * Updated a bunch of ports for tests * plugged in code to create reroutes that are aggregates * made multiplexer a class * hacked test to death * simple aggregator done, initial validation done * removed request id from context, it is still specific for http request * now aggregates to json always * docs for aggregate reroutes * Updated docs
This commit is contained in:
parent
2573657ec2
commit
d1926268ac
@ -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
|
||||||
|
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?
|
||||||
|
|
@ -18,13 +18,13 @@ 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/caching
|
features/caching
|
||||||
features/qualityofservice
|
features/qualityofservice
|
||||||
features/headerstransformation
|
features/headerstransformation
|
||||||
@ -35,7 +35,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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,5 @@
|
|||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using Ocelot.Middleware.Multiplexer;
|
||||||
|
|
||||||
namespace Ocelot.DependencyInjection
|
namespace Ocelot.DependencyInjection
|
||||||
{
|
{
|
||||||
@ -30,7 +31,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 +114,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>();
|
||||||
@ -149,6 +148,8 @@ namespace Ocelot.DependencyInjection
|
|||||||
_provider = new DelegatingHandlerHandlerProvider();
|
_provider = new DelegatingHandlerHandlerProvider();
|
||||||
_services.TryAddSingleton<IDelegatingHandlerHandlerProvider>(_provider);
|
_services.TryAddSingleton<IDelegatingHandlerHandlerProvider>(_provider);
|
||||||
_services.AddTransient<ITracingHandler, NoTracingHandler>();
|
_services.AddTransient<ITracingHandler, NoTracingHandler>();
|
||||||
|
_services.TryAddSingleton<IMultiplexer, Multiplexer>();
|
||||||
|
_services.TryAddSingleton<IResponseAggregator, SimpleJsonResponseAggregator>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IOcelotAdministrationBuilder AddAdministration(string path, string secret)
|
public IOcelotAdministrationBuilder AddAdministration(string path, string secret)
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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,5 +1,8 @@
|
|||||||
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
|
||||||
{
|
{
|
||||||
@ -8,15 +11,20 @@ namespace Ocelot.Requester
|
|||||||
private readonly ITracingHandler _tracingHandler;
|
private readonly ITracingHandler _tracingHandler;
|
||||||
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,
|
||||||
|
ITracingHandler tracingHandler,
|
||||||
|
IQosProviderHouse qosProviderHouse)
|
||||||
{
|
{
|
||||||
_tracingHandler = tracingHandler;
|
_tracingHandler = tracingHandler;
|
||||||
_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 +35,24 @@ namespace Ocelot.Requester
|
|||||||
provider.Add(handler);
|
provider.Add(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.IsTracing)
|
if (request.HttpHandlerOptions.UseTracing)
|
||||||
{
|
{
|
||||||
provider.Add(() => (DelegatingHandler)_tracingHandler);
|
provider.Add(() => (DelegatingHandler)_tracingHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
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,16 +13,16 @@ 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);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>();
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -37,7 +37,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 51879,
|
Port = 51899,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
@ -51,13 +51,13 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", 200, "Hello from Laura"))
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51899", 200, "Hello from Laura"))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunning())
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
.Given(x => x.GivenTheServiceNowReturns("http://localhost:51879", 200, "Hello from Tom"))
|
.Given(x => x.GivenTheServiceNowReturns("http://localhost:51899", 200, "Hello from Tom"))
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
@ -80,7 +80,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 51879,
|
Port = 51899,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
@ -94,13 +94,13 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", 200, "Hello from Laura"))
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51899", 200, "Hello from Laura"))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunningUsingJsonSerializedCache())
|
.And(x => _steps.GivenOcelotIsRunningUsingJsonSerializedCache())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
.Given(x => x.GivenTheServiceNowReturns("http://localhost:51879", 200, "Hello from Tom"))
|
.Given(x => x.GivenTheServiceNowReturns("http://localhost:51899", 200, "Hello from Tom"))
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
@ -122,7 +122,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 51879,
|
Port = 51899,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
@ -136,13 +136,13 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", 200, "Hello from Laura"))
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51899", 200, "Hello from Laura"))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunning())
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
.Given(x => x.GivenTheServiceNowReturns("http://localhost:51879", 200, "Hello from Tom"))
|
.Given(x => x.GivenTheServiceNowReturns("http://localhost:51899", 200, "Hello from Tom"))
|
||||||
.And(x => x.GivenTheCacheExpires())
|
.And(x => x.GivenTheCacheExpires())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
@ -36,7 +36,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 51879,
|
Port = 51877,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
@ -46,7 +46,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product"))
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51877", "/api/products/1", 200, "Some Product"))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunning())
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1"))
|
||||||
@ -69,7 +69,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 51879,
|
Port = 51877,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
@ -80,7 +80,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product"))
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51877", "/api/products/1", 200, "Some Product"))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunning())
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1"))
|
||||||
@ -103,7 +103,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 51879,
|
Port = 51877,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
@ -114,7 +114,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product"))
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51877", "/api/products/1", 200, "Some Product"))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunning())
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1"))
|
||||||
@ -137,7 +137,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 51879,
|
Port = 51877,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
@ -148,7 +148,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product"))
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51877", "/api/products/1", 200, "Some Product"))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunning())
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1"))
|
||||||
@ -171,7 +171,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 51879,
|
Port = 51877,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
@ -182,7 +182,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product"))
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51877", "/api/products/1", 200, "Some Product"))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunning())
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1"))
|
||||||
@ -205,7 +205,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 51879,
|
Port = 51877,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
@ -216,7 +216,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product"))
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51877", "/api/products/1", 200, "Some Product"))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunning())
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1"))
|
||||||
|
@ -48,7 +48,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 51879,
|
Port = 51876,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
@ -81,7 +81,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/ClientRateLimit"))
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", "/api/ClientRateLimit"))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunning())
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit",1))
|
.When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit",1))
|
||||||
@ -109,7 +109,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 51879,
|
Port = 51876,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
@ -140,7 +140,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/ClientRateLimit"))
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", "/api/ClientRateLimit"))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunning())
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit", 4))
|
.When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit", 4))
|
||||||
|
@ -32,7 +32,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void should_call_pre_query_string_builder_middleware()
|
public void should_call_pre_query_string_builder_middleware()
|
||||||
{
|
{
|
||||||
var configuration = new OcelotMiddlewareConfiguration
|
var configuration = new OcelotPipelineConfiguration
|
||||||
{
|
{
|
||||||
AuthorisationMiddleware = async (ctx, next) =>
|
AuthorisationMiddleware = async (ctx, next) =>
|
||||||
{
|
{
|
||||||
@ -75,7 +75,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void should_call_authorisation_middleware()
|
public void should_call_authorisation_middleware()
|
||||||
{
|
{
|
||||||
var configuration = new OcelotMiddlewareConfiguration
|
var configuration = new OcelotPipelineConfiguration
|
||||||
{
|
{
|
||||||
AuthorisationMiddleware = async (ctx, next) =>
|
AuthorisationMiddleware = async (ctx, next) =>
|
||||||
{
|
{
|
||||||
@ -118,7 +118,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void should_call_authentication_middleware()
|
public void should_call_authentication_middleware()
|
||||||
{
|
{
|
||||||
var configuration = new OcelotMiddlewareConfiguration
|
var configuration = new OcelotPipelineConfiguration
|
||||||
{
|
{
|
||||||
AuthenticationMiddleware = async (ctx, next) =>
|
AuthenticationMiddleware = async (ctx, next) =>
|
||||||
{
|
{
|
||||||
@ -161,7 +161,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void should_call_pre_error_middleware()
|
public void should_call_pre_error_middleware()
|
||||||
{
|
{
|
||||||
var configuration = new OcelotMiddlewareConfiguration
|
var configuration = new OcelotPipelineConfiguration
|
||||||
{
|
{
|
||||||
PreErrorResponderMiddleware = async (ctx, next) =>
|
PreErrorResponderMiddleware = async (ctx, next) =>
|
||||||
{
|
{
|
||||||
@ -204,7 +204,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void should_call_pre_authorisation_middleware()
|
public void should_call_pre_authorisation_middleware()
|
||||||
{
|
{
|
||||||
var configuration = new OcelotMiddlewareConfiguration
|
var configuration = new OcelotPipelineConfiguration
|
||||||
{
|
{
|
||||||
PreAuthorisationMiddleware = async (ctx, next) =>
|
PreAuthorisationMiddleware = async (ctx, next) =>
|
||||||
{
|
{
|
||||||
@ -247,7 +247,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void should_call_pre_http_authentication_middleware()
|
public void should_call_pre_http_authentication_middleware()
|
||||||
{
|
{
|
||||||
var configuration = new OcelotMiddlewareConfiguration
|
var configuration = new OcelotPipelineConfiguration
|
||||||
{
|
{
|
||||||
PreAuthenticationMiddleware = async (ctx, next) =>
|
PreAuthenticationMiddleware = async (ctx, next) =>
|
||||||
{
|
{
|
||||||
|
@ -41,7 +41,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 51879,
|
Port = 51871,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UpstreamPathTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
@ -54,7 +54,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Laz"))
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51871", "/", 200, "Laz"))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunning())
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
.And(x => _steps.GivenIAddAHeader("Laz", "D"))
|
.And(x => _steps.GivenIAddAHeader("Laz", "D"))
|
||||||
@ -80,7 +80,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 51879,
|
Port = 51871,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UpstreamPathTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
@ -93,7 +93,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Location", "http://www.bbc.co.uk/"))
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51871", "/", 200, "Location", "http://www.bbc.co.uk/"))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunning())
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
@ -41,7 +41,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 51879,
|
Port = 51872,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UpstreamPathTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
@ -57,7 +57,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAPossiblyBrokenServiceRunningOn("http://localhost:51879", "Hello from Laura"))
|
this.Given(x => x.GivenThereIsAPossiblyBrokenServiceRunningOn("http://localhost:51872", "Hello from Laura"))
|
||||||
.Given(x => _steps.GivenThereIsAConfiguration(configuration))
|
.Given(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.Given(x => _steps.GivenOcelotIsRunning())
|
.Given(x => _steps.GivenOcelotIsRunning())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
@ -92,7 +92,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 51879,
|
Port = 51872,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UpstreamPathTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
@ -122,7 +122,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAPossiblyBrokenServiceRunningOn("http://localhost:51879", "Hello from Laura"))
|
this.Given(x => x.GivenThereIsAPossiblyBrokenServiceRunningOn("http://localhost:51872", "Hello from Laura"))
|
||||||
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51880/", 200, "Hello from Tom"))
|
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51880/", 200, "Hello from Tom"))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunning())
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
|
@ -39,7 +39,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 51879,
|
Port = 51873,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
@ -50,7 +50,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879"))
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51873"))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunning())
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
@ -73,7 +73,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 51879,
|
Port = 51873,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
@ -85,7 +85,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
|
|
||||||
var requestId = Guid.NewGuid().ToString();
|
var requestId = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879"))
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51873"))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunning())
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/", requestId))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/", requestId))
|
||||||
@ -108,7 +108,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 51879,
|
Port = 51873,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
@ -124,7 +124,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
|
|
||||||
var requestId = Guid.NewGuid().ToString();
|
var requestId = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879"))
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51873"))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunning())
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/", requestId))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/", requestId))
|
||||||
|
@ -298,7 +298,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 51879,
|
Port = 51874,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UpstreamPathTemplate = "/vacancy/",
|
UpstreamPathTemplate = "/vacancy/",
|
||||||
@ -315,7 +315,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 51879,
|
Port = 51874,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UpstreamPathTemplate = "/vacancy/{vacancyId}",
|
UpstreamPathTemplate = "/vacancy/{vacancyId}",
|
||||||
@ -326,7 +326,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/v1/vacancy/1", 200, "Hello from Laura"))
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51874", "/api/v1/vacancy/1", 200, "Hello from Laura"))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunning())
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/vacancy/1"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/vacancy/1"))
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
@ -336,7 +338,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is annoying cos it should be in the constructor but we need to set up the file before calling startup so its a step.
|
/// This is annoying cos it should be in the constructor but we need to set up the file before calling startup so its a step.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void GivenOcelotIsRunning(OcelotMiddlewareConfiguration ocelotMiddlewareConfig)
|
public void GivenOcelotIsRunning(OcelotPipelineConfiguration ocelotPipelineConfig)
|
||||||
{
|
{
|
||||||
var builder = new ConfigurationBuilder()
|
var builder = new ConfigurationBuilder()
|
||||||
.SetBasePath(Directory.GetCurrentDirectory())
|
.SetBasePath(Directory.GetCurrentDirectory())
|
||||||
@ -373,7 +375,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
})
|
})
|
||||||
.Configure(a =>
|
.Configure(a =>
|
||||||
{
|
{
|
||||||
a.UseOcelot(ocelotMiddlewareConfig).Wait();
|
a.UseOcelot(ocelotPipelineConfig).Wait();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
_ocelotClient = _ocelotServer.CreateClient();
|
_ocelotClient = _ocelotServer.CreateClient();
|
||||||
@ -552,7 +554,6 @@ namespace Ocelot.AcceptanceTests
|
|||||||
_response.StatusCode.ShouldBe(expectedHttpStatusCode);
|
_response.StatusCode.ShouldBe(expectedHttpStatusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void ThenTheStatusCodeShouldBe(int expectedHttpStatusCode)
|
public void ThenTheStatusCodeShouldBe(int expectedHttpStatusCode)
|
||||||
{
|
{
|
||||||
var responseStatusCode = (int)_response.StatusCode;
|
var responseStatusCode = (int)_response.StatusCode;
|
||||||
@ -579,5 +580,51 @@ namespace Ocelot.AcceptanceTests
|
|||||||
{
|
{
|
||||||
_response.Content.Headers.ContentLength.ShouldBe(expected);
|
_response.Content.Headers.ContentLength.ShouldBe(expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void WhenIMakeLotsOfDifferentRequestsToTheApiGateway()
|
||||||
|
{
|
||||||
|
int numberOfRequests = 100;
|
||||||
|
var aggregateUrl = "/";
|
||||||
|
var aggregateExpected = "{\"Laura\":{Hello from Laura},\"Tom\":{Hello from Tom}}";
|
||||||
|
var tomUrl = "/tom";
|
||||||
|
var tomExpected = "{Hello from Tom}";
|
||||||
|
var lauraUrl = "/laura";
|
||||||
|
var lauraExpected = "{Hello from Laura}";
|
||||||
|
var random = new Random();
|
||||||
|
|
||||||
|
var aggregateTasks = new Task[numberOfRequests];
|
||||||
|
|
||||||
|
for (int i = 0; i < numberOfRequests; i++)
|
||||||
|
{
|
||||||
|
aggregateTasks[i] = Fire(aggregateUrl, aggregateExpected, random);
|
||||||
|
}
|
||||||
|
|
||||||
|
var tomTasks = new Task[numberOfRequests];
|
||||||
|
|
||||||
|
for (int i = 0; i < numberOfRequests; i++)
|
||||||
|
{
|
||||||
|
tomTasks[i] = Fire(tomUrl, tomExpected, random);
|
||||||
|
}
|
||||||
|
|
||||||
|
var lauraTasks = new Task[numberOfRequests];
|
||||||
|
|
||||||
|
for (int i = 0; i < numberOfRequests; i++)
|
||||||
|
{
|
||||||
|
lauraTasks[i] = Fire(lauraUrl, lauraExpected, random);
|
||||||
|
}
|
||||||
|
|
||||||
|
Task.WaitAll(lauraTasks);
|
||||||
|
Task.WaitAll(tomTasks);
|
||||||
|
Task.WaitAll(aggregateTasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Fire(string url, string expectedBody, Random random)
|
||||||
|
{
|
||||||
|
var request = new HttpRequestMessage(new HttpMethod("GET"), url);
|
||||||
|
await Task.Delay(random.Next(0, 2));
|
||||||
|
var response = await _ocelotClient.SendAsync(request);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
content.ShouldBe(expectedBody);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 51879,
|
Port = 51875,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UpstreamPathTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
@ -49,7 +49,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Hello from Laura"))
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51875", "/", 200, "Hello from Laura"))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunning())
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
@ -75,7 +75,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 51879,
|
Port = 51875,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UpstreamPathTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
@ -101,7 +101,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Hello from Laura"))
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51875", "/", 200, "Hello from Laura"))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunning())
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
@ -142,7 +142,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 51879,
|
Port = 51875,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UpstreamPathTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
@ -152,7 +152,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Hello from Laura"))
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51875", "/", 200, "Hello from Laura"))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunning())
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
@ -192,7 +192,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 51879,
|
Port = 51875,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UpstreamPathTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
@ -202,7 +202,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Hello from Laura"))
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51875", "/", 200, "Hello from Laura"))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunning())
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
@ -227,7 +227,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 51879,
|
Port = 51875,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UpstreamPathTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
@ -237,7 +237,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Hello from Laura"))
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51875", "/", 200, "Hello from Laura"))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunning())
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BenchmarkDotNet" Version="0.10.9" />
|
<PackageReference Include="BenchmarkDotNet" Version="0.10.12" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -17,7 +17,7 @@ namespace Ocelot.Benchmarks
|
|||||||
Add(StatisticColumn.AllStatistics);
|
Add(StatisticColumn.AllStatistics);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Setup]
|
[GlobalSetup]
|
||||||
public void SetUp()
|
public void SetUp()
|
||||||
{
|
{
|
||||||
_urlPathMatcher = new RegExUrlMatcher();
|
_urlPathMatcher = new RegExUrlMatcher();
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
"HttpHandlerOptions": {
|
"HttpHandlerOptions": {
|
||||||
"AllowAutoRedirect": true,
|
"AllowAutoRedirect": true,
|
||||||
"UseCookieContainer": true,
|
"UseCookieContainer": true,
|
||||||
"UseTracing": true
|
"UseTracing": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -99,7 +99,7 @@
|
|||||||
"HttpHandlerOptions": {
|
"HttpHandlerOptions": {
|
||||||
"AllowAutoRedirect": true,
|
"AllowAutoRedirect": true,
|
||||||
"UseCookieContainer": true,
|
"UseCookieContainer": true,
|
||||||
"UseTracing": true
|
"UseTracing": false
|
||||||
},
|
},
|
||||||
"QoSOptions": {
|
"QoSOptions": {
|
||||||
"ExceptionsAllowedBeforeBreaking": 3,
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
@ -121,7 +121,7 @@
|
|||||||
"HttpHandlerOptions": {
|
"HttpHandlerOptions": {
|
||||||
"AllowAutoRedirect": true,
|
"AllowAutoRedirect": true,
|
||||||
"UseCookieContainer": true,
|
"UseCookieContainer": true,
|
||||||
"UseTracing": true
|
"UseTracing": false
|
||||||
},
|
},
|
||||||
"QoSOptions": {
|
"QoSOptions": {
|
||||||
"ExceptionsAllowedBeforeBreaking": 3,
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
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