diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 00000000..49a79976
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,46 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at tom@threemammals.co.uk. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
+
+[homepage]: http://contributor-covenant.org
+[version]: http://contributor-covenant.org/version/1/4/
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000..e6f89046
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,9 @@
+We love to receive contributions from the community so please keep them coming :)
+
+Pull requests, issues and commentary welcome!
+
+Please complete the relavent template for issues and PRs. Sometimes it's worth getting in touch with us to discuss changes
+before doing any work incase this is something we are already doing or it might not make sense. We can also give
+advice on the easiest way to do things :)
+
+Finally we mark all existing issues as help wanted, small, medium and large effort. If you want to contriute for the first time I suggest looking at a help wanted & small effort issue :)
diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..3f0d0f3a
--- /dev/null
+++ b/ISSUE_TEMPLATE.md
@@ -0,0 +1,17 @@
+## Expected Behavior / New Feature
+
+
+## Actual Behavior / Motivation for New Feautre
+
+
+## Steps to Reproduce the Problem
+
+ 1.
+ 1.
+ 1.
+
+## Specifications
+
+ - Version:
+ - Platform:
+ - Subsystem:
diff --git a/Ocelot.sln b/Ocelot.sln
index fb7008d3..6aa429fe 100644
--- a/Ocelot.sln
+++ b/Ocelot.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
-VisualStudioVersion = 15.0.27130.2024
+VisualStudioVersion = 15.0.27130.2036
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5CFB79B7-C9DC-45A4-9A75-625D92471702}"
EndProject
diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 00000000..72a3b6fe
--- /dev/null
+++ b/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,7 @@
+Fixes / New Feature #
+
+## Proposed Changes
+
+ -
+ -
+ -
diff --git a/README.md b/README.md
index 7145dc38..fc9b67b8 100644
--- a/README.md
+++ b/README.md
@@ -1,15 +1,17 @@
-# Ocelot
+[](http://threemammals.com/ocelot)
[](https://ci.appveyor.com/project/TomPallister/ocelot-fcfpb)
[](https://ci.appveyor.com/project/TomPallister/ocelot-fcfpb/history?branch=develop)
-
[](https://coveralls.io/github/TomPallister/Ocelot?branch=develop)
+# Ocelot
+
Ocelot is a .NET Api Gateway. This project is aimed at people using .NET running
a micro services / service orientated architecture
-that need a unified point of entry into their system.
+that need a unified point of entry into their system. However it will worth with anything that
+speaks HTTP and run on any platform that asp.net core supports.
In particular I want easy integration with
IdentityServer reference and bearer tokens.
@@ -26,11 +28,28 @@ Ocelot manipulates the HttpRequest object into a state specified by its configur
it reaches a request builder middleware where it creates a HttpRequestMessage object which is
used to make a request to a downstream service. The middleware that makes the request is
the last thing in the Ocelot pipeline. It does not call the next middleware.
-The response from the downstream service is stored in a per request scoped repository
-and retrieved as the requests goes back up the Ocelot pipeline. There is a piece of middleware
-that maps the HttpResponseMessage onto the HttpResponse object and that is returned to the client.
-That is basically it with a bunch of other features.
-
+The response from the downstream service is retrieved as the requests goes back up the Ocelot pipeline.
+There is a piece of middleware that maps the HttpResponseMessage onto the HttpResponse object and that
+is returned to the client. That is basically it with a bunch of other features!
+
+## Features
+
+A quick list of Ocelot's capabilities for more information see the [documentation](http://ocelot.readthedocs.io/en/latest/).
+
+* Routing
+* Request Aggregation
+* Service Discovery with Consul
+* Authentication
+* Authorisation
+* Rate Limiting
+* Caching
+* Retry policies / QoS
+* Load Balancing
+* Logging / Tracing / Correlation
+* Headers / Query String / Claims Transformation
+* Custom Middleware / Delegating Handlers
+* Configuration / Administration REST API
+
## How to install
Ocelot is designed to work with ASP.NET core only and is currently
@@ -48,20 +67,22 @@ Please click [here](http://ocelot.readthedocs.io/en/latest/) for the Ocleot docu
## Coming up
-You can see what we are working on [here](https://github.com/TomPallister/Ocelot/projects/1)
+You can see what we are working on [here](https://github.com/ThreeMammals/Ocelot/issues).
## Contributing
-Pull requests, issues and commentary welcome! No special process just create a request and get in
-touch either via gitter or create an issue.
+We love to receive contributions from the community so please keep them coming :)
+Pull requests, issues and commentary welcome!
+
+Please complete the relavent template for issues and PRs. Sometimes it's worth getting in touch with us to discuss changes
+before doing any work incase this is something we are already doing or it might not make sense. We can also give
+advice on the easiest way to do things :)
+
+Finally we mark all existing issues as help wanted, small, medium and large effort. If you want to contriute for the first time I suggest looking at a help wanted & small effort issue :)
## Things that are currently annoying me
-+ The base OcelotMiddleware lets you access things that are going to be null
-and doesnt check the response is OK. I think the fact you can even call stuff
-that isnt available is annoying. Let alone it be null.
-
[ Get more details at **codescene.io**.](https://codescene.io/projects/697/jobs/latest-successful/results)
diff --git a/docs/favicon.ico b/docs/favicon.ico
index ee470e42..5e606f74 100644
Binary files a/docs/favicon.ico and b/docs/favicon.ico differ
diff --git a/docs/features/administration.rst b/docs/features/administration.rst
index 425c958e..dd08feab 100644
--- a/docs/features/administration.rst
+++ b/docs/features/administration.rst
@@ -21,7 +21,7 @@ All you need to do to hook into your own IdentityServer is add the following to
};
services
- .AddOcelot(Configuration)
+ .AddOcelot()
.AddAdministration("/administration", options);
}
@@ -51,7 +51,7 @@ The secret is the client secret that Ocelot's internal IdentityServer will use t
public virtual void ConfigureServices(IServiceCollection services)
{
services
- .AddOcelot(Configuration)
+ .AddOcelot()
.AddAdministration("/administration", "secret");
}
diff --git a/docs/features/authentication.rst b/docs/features/authentication.rst
index dd78fcfa..78aa890f 100644
--- a/docs/features/authentication.rst
+++ b/docs/features/authentication.rst
@@ -63,7 +63,7 @@ If you want to authenticate using JWT tokens maybe from a provider like Auth0 yo
x.Audience = "test";
});
- services.AddOcelot(Configuration);
+ services.AddOcelot();
}
Then map the authentication provider key to a ReRoute in your configuration e.g.
@@ -111,7 +111,7 @@ In order to use IdentityServer bearer tokens register your IdentityServer servic
services.AddAuthentication()
.AddIdentityServerAuthentication(authenticationProviderKey, options);
- services.AddOcelot(Configuration);
+ services.AddOcelot();
}
Then map the authentication provider key to a ReRoute in your configuration e.g.
diff --git a/docs/features/configuration.rst b/docs/features/configuration.rst
index 3bd4da2f..93cae138 100644
--- a/docs/features/configuration.rst
+++ b/docs/features/configuration.rst
@@ -78,6 +78,29 @@ Set it true if the request should automatically follow redirection responses fro
- _UseCookieContainer_ is a value that indicates whether the handler uses the CookieContainer property to store server cookies and uses these cookies when sending requests.
The default value is true.
+Multiple environments
+^^^^^^^^^^^^^^^^^^^^^
+
+Like any other asp.net core project Ocelot supports configuration file names such as configuration.dev.json, configuration.test.json etc. In order to implement this add the following
+to you
+
+.. code-block:: csharp
+
+ .ConfigureAppConfiguration((hostingContext, config) =>
+ {
+ config
+ .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
+ .AddJsonFile("appsettings.json", true, true)
+ .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
+ .AddJsonFile("configuration.json")
+ .AddJsonFile($"configuration.{hostingContext.HostingEnvironment.EnvironmentName}.json")
+ .AddEnvironmentVariables();
+ })
+
+Ocelot should now use the environment specific configuration and fall back to configuration.json if there isnt one.
+
+You also need to set the corresponding environment variable which is ASPNETCORE_ENVIRONMENT. More info on this can be found in the `asp.net core docs `_.
+
Store configuration in consul
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -86,7 +109,7 @@ If you add the following when you register your services Ocelot will attempt to
.. code-block:: csharp
services
- .AddOcelot(Configuration)
+ .AddOcelot()
.AddStoreOcelotConfigurationInConsul();
You also need to add the following to your configuration.json. This is how Ocelot
diff --git a/docs/features/raft.rst b/docs/features/raft.rst
index 45ea59f2..dd0cf031 100644
--- a/docs/features/raft.rst
+++ b/docs/features/raft.rst
@@ -12,7 +12,7 @@ In order to enable Rafty in Ocelot you must make the following changes to your S
public virtual void ConfigureServices(IServiceCollection services)
{
services
- .AddOcelot(Configuration)
+ .AddOcelot()
.AddAdministration("/administration", "secret")
.AddRafty();
}
diff --git a/docs/features/ratelimiting.rst b/docs/features/ratelimiting.rst
new file mode 100644
index 00000000..d929c905
--- /dev/null
+++ b/docs/features/ratelimiting.rst
@@ -0,0 +1,40 @@
+Rate Limiting
+=============
+
+Thanks to `@catcherwong article `_ for inspiring me to finally write this documentation.
+
+Ocelot supports rate limiting of upstream requests so that your downstream services do not become overloaded. This feature was added by @geffzhang on GitHub! Thanks very much.
+
+OK so to get rate limiting working for a ReRoute you need to add the following json to it.
+
+.. code-block:: json
+
+ "RateLimitOptions": {
+ "ClientWhitelist": [],
+ "EnableRateLimiting": true,
+ "Period": "1s",
+ "PeriodTimespan": 1,
+ "Limit": 1
+ }
+
+ClientWhitelist - This is an array that contains the whitelist of the client. It means that the client in this array will not be affected by the rate limiting.
+EnableRateLimiting - This value specifies enable endpoint rate limiting.
+Period - This value specifies the period, such as 1s, 5m, 1h,1d and so on.
+PeriodTimespan - This value specifies that we can retry after a certain number of seconds.
+Limit - This value specifies the maximum number of requests that a client can make in a defined period.
+
+You can also set the following in the GlobalConfiguration part of configuration.json
+
+.. code-block:: json
+
+ "RateLimitOptions": {
+ "DisableRateLimitHeaders": false,
+ "QuotaExceededMessage": "Customize Tips!",
+ "HttpStatusCode": 999,
+ "ClientIdHeader" : "Test"
+ }
+
+DisableRateLimitHeaders - This value specifies whether X-Rate-Limit and Rety-After headers are disabled.
+QuotaExceededMessage - This value specifies the exceeded message.
+HttpStatusCode - This value specifies the returned HTTP Status code when rate limiting occurs.
+ClientIdHeader - Allows you to specifiy the header that should be used to identify clients. By default it is "ClientId"
diff --git a/docs/features/requestaggregation.rst b/docs/features/requestaggregation.rst
new file mode 100644
index 00000000..b359eb72
--- /dev/null
+++ b/docs/features/requestaggregation.rst
@@ -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 `_ .
+
+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?
+
diff --git a/docs/features/tracing.rst b/docs/features/tracing.rst
index a30ea741..0a896d45 100644
--- a/docs/features/tracing.rst
+++ b/docs/features/tracing.rst
@@ -12,7 +12,7 @@ In your ConfigureServices method
.. code-block:: csharp
services
- .AddOcelot(Configuration)
+ .AddOcelot()
.AddOpenTracing(option =>
{
//this is the url that the butterfly collector server is running on...
diff --git a/docs/index.rst b/docs/index.rst
index c98c2174..258085ef 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -18,13 +18,14 @@ Thanks for taking a look at the Ocelot documentation. Please use the left hand n
:hidden:
:caption: Features
- features/routing
features/configuration
+ features/routing
+ features/requestaggregation
features/servicediscovery
features/authentication
features/authorisation
features/administration
- features/raft
+ features/ratelimiting
features/caching
features/qualityofservice
features/headerstransformation
@@ -35,7 +36,7 @@ Thanks for taking a look at the Ocelot documentation. Please use the left hand n
features/middlewareinjection
features/loadbalancer
features/delegatinghandlers
-
+ features/raft
.. toctree::
:maxdepth: 2
diff --git a/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs b/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs
index 1d820600..ffdd1ac6 100644
--- a/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs
+++ b/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs
@@ -1,12 +1,9 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Http;
using Ocelot.Configuration;
using Ocelot.Errors;
using Ocelot.Infrastructure.Extensions;
-using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging;
using Ocelot.Middleware;
@@ -14,35 +11,29 @@ namespace Ocelot.Authentication.Middleware
{
public class AuthenticationMiddleware : OcelotMiddleware
{
- private readonly RequestDelegate _next;
- private readonly IApplicationBuilder _app;
- private readonly IAuthenticationSchemeProvider _authSchemeProvider;
+ private readonly OcelotRequestDelegate _next;
private readonly IOcelotLogger _logger;
- public AuthenticationMiddleware(RequestDelegate next,
- IApplicationBuilder app,
- IRequestScopedDataRepository requestScopedDataRepository,
+ public AuthenticationMiddleware(OcelotRequestDelegate next,
IOcelotLoggerFactory loggerFactory)
- : base(requestScopedDataRepository)
{
_next = next;
- _app = app;
_logger = loggerFactory.CreateLogger();
}
- 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);
}
else
@@ -50,22 +41,23 @@ namespace Ocelot.Authentication.Middleware
var error = new List
{
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()}");
- SetPipelineError(error);
+ _logger.LogError($"Client has NOT been authenticated for {context.HttpContext.Request.Path} and pipeline error set. {error.ToErrorString()}");
+
+ SetPipelineError(context, error);
}
}
else
{
- _logger.LogTrace($"No authentication needed for {context.Request.Path}");
+ _logger.LogTrace($"No authentication needed for {context.HttpContext.Request.Path}");
await _next.Invoke(context);
}
}
- private static bool IsAuthenticatedRoute(ReRoute reRoute)
+ private static bool IsAuthenticatedRoute(DownstreamReRoute reRoute)
{
return reRoute.IsAuthenticated;
}
diff --git a/src/Ocelot/Authentication/Middleware/AuthenticationMiddlewareMiddlewareExtensions.cs b/src/Ocelot/Authentication/Middleware/AuthenticationMiddlewareMiddlewareExtensions.cs
index 4539ba25..868b668b 100644
--- a/src/Ocelot/Authentication/Middleware/AuthenticationMiddlewareMiddlewareExtensions.cs
+++ b/src/Ocelot/Authentication/Middleware/AuthenticationMiddlewareMiddlewareExtensions.cs
@@ -1,12 +1,13 @@
using Microsoft.AspNetCore.Builder;
+using Ocelot.Middleware.Pipeline;
namespace Ocelot.Authentication.Middleware
{
public static class AuthenticationMiddlewareMiddlewareExtensions
{
- public static IApplicationBuilder UseAuthenticationMiddleware(this IApplicationBuilder builder)
+ public static IOcelotPipelineBuilder UseAuthenticationMiddleware(this IOcelotPipelineBuilder builder)
{
- return builder.UseMiddleware(builder);
+ return builder.UseMiddleware();
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs b/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs
index f3f55e35..f6c19522 100644
--- a/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs
+++ b/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs
@@ -9,21 +9,20 @@ namespace Ocelot.Authorisation.Middleware
using System.Threading.Tasks;
using Errors;
using Microsoft.AspNetCore.Http;
+ using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.Middleware;
public class AuthorisationMiddleware : OcelotMiddleware
{
- private readonly RequestDelegate _next;
+ private readonly OcelotRequestDelegate _next;
private readonly IClaimsAuthoriser _claimsAuthoriser;
private readonly IScopesAuthoriser _scopesAuthoriser;
private readonly IOcelotLogger _logger;
- public AuthorisationMiddleware(RequestDelegate next,
- IRequestScopedDataRepository requestScopedDataRepository,
+ public AuthorisationMiddleware(OcelotRequestDelegate next,
IClaimsAuthoriser claimsAuthoriser,
IScopesAuthoriser scopesAuthoriser,
IOcelotLoggerFactory loggerFactory)
- : base(requestScopedDataRepository)
{
_next = next;
_claimsAuthoriser = claimsAuthoriser;
@@ -31,19 +30,19 @@ namespace Ocelot.Authorisation.Middleware
_logger = loggerFactory.CreateLogger();
}
- 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");
- var authorised = _scopesAuthoriser.Authorise(context.User, DownstreamRoute.ReRoute.AuthenticationOptions.AllowedScopes);
+ var authorised = _scopesAuthoriser.Authorise(context.HttpContext.User, context.DownstreamReRoute.AuthenticationOptions.AllowedScopes);
if (authorised.IsError)
{
_logger.LogDebug("error authorising user scopes");
- SetPipelineError(authorised.Errors);
+ SetPipelineError(context, authorised.Errors);
return;
}
@@ -55,46 +54,46 @@ namespace Ocelot.Authorisation.Middleware
{
_logger.LogDebug("user scopes is not authorised setting pipeline error");
- SetPipelineError(new List
+ SetPipelineError(context, new List
{
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");
- var authorised = _claimsAuthoriser.Authorise(context.User, DownstreamRoute.ReRoute.RouteClaimsRequirement);
+ var authorised = _claimsAuthoriser.Authorise(context.HttpContext.User, context.DownstreamReRoute.RouteClaimsRequirement);
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;
}
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);
}
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
+ SetPipelineError(context, new List
{
- 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
{
- _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);
}
}
@@ -104,12 +103,12 @@ namespace Ocelot.Authorisation.Middleware
return authorised.Data;
}
- private static bool IsAuthenticatedRoute(ReRoute reRoute)
+ private static bool IsAuthenticatedRoute(DownstreamReRoute reRoute)
{
return reRoute.IsAuthenticated;
}
- private static bool IsAuthorisedRoute(ReRoute reRoute)
+ private static bool IsAuthorisedRoute(DownstreamReRoute reRoute)
{
return reRoute.IsAuthorised;
}
diff --git a/src/Ocelot/Authorisation/Middleware/AuthorisationMiddlewareMiddlewareExtensions.cs b/src/Ocelot/Authorisation/Middleware/AuthorisationMiddlewareMiddlewareExtensions.cs
index ca1fc945..ffc30177 100644
--- a/src/Ocelot/Authorisation/Middleware/AuthorisationMiddlewareMiddlewareExtensions.cs
+++ b/src/Ocelot/Authorisation/Middleware/AuthorisationMiddlewareMiddlewareExtensions.cs
@@ -1,12 +1,14 @@
+using Ocelot.Middleware.Pipeline;
+
namespace Ocelot.Authorisation.Middleware
{
using Microsoft.AspNetCore.Builder;
public static class AuthorisationMiddlewareMiddlewareExtensions
{
- public static IApplicationBuilder UseAuthorisationMiddleware(this IApplicationBuilder builder)
+ public static IOcelotPipelineBuilder UseAuthorisationMiddleware(this IOcelotPipelineBuilder builder)
{
return builder.UseMiddleware();
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs b/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs
index 07f7445e..873c4cf7 100644
--- a/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs
+++ b/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs
@@ -7,22 +7,21 @@ using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging;
using Ocelot.Middleware;
using System.IO;
+using Ocelot.DownstreamRouteFinder.Middleware;
namespace Ocelot.Cache.Middleware
{
public class OutputCacheMiddleware : OcelotMiddleware
{
- private readonly RequestDelegate _next;
+ private readonly OcelotRequestDelegate _next;
private readonly IOcelotLogger _logger;
private readonly IOcelotCache _outputCache;
private readonly IRegionCreator _regionCreator;
- public OutputCacheMiddleware(RequestDelegate next,
+ public OutputCacheMiddleware(OcelotRequestDelegate next,
IOcelotLoggerFactory loggerFactory,
- IRequestScopedDataRepository scopedDataRepository,
IOcelotCache outputCache,
IRegionCreator regionCreator)
- : base(scopedDataRepository)
{
_next = next;
_outputCache = outputCache;
@@ -30,26 +29,26 @@ namespace Ocelot.Cache.Middleware
_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);
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);
- var cached = _outputCache.Get(downstreamUrlKey, DownstreamRoute.ReRoute.CacheOptions.Region);
+ var cached = _outputCache.Get(downstreamUrlKey, context.DownstreamReRoute.CacheOptions.Region);
if (cached != null)
{
_logger.LogDebug("cache entry exists for {downstreamUrlKey}", downstreamUrlKey);
var response = CreateHttpResponseMessage(cached);
- SetHttpResponseMessageThisRequest(response);
+ SetHttpResponseMessageThisRequest(context, response);
_logger.LogDebug("finished returned cached response for {downstreamUrlKey}", downstreamUrlKey);
@@ -60,20 +59,25 @@ namespace Ocelot.Cache.Middleware
await _next.Invoke(context);
- if (PipelineError)
+ if (context.IsError)
{
_logger.LogDebug("there was a pipeline error for {downstreamUrlKey}", downstreamUrlKey);
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);
}
+ private void SetHttpResponseMessageThisRequest(DownstreamContext context, HttpResponseMessage response)
+ {
+ context.DownstreamResponse = response;
+ }
+
internal HttpResponseMessage CreateHttpResponseMessage(CachedResponse cached)
{
if (cached == null)
diff --git a/src/Ocelot/Cache/Middleware/OutputCacheMiddlewareExtensions.cs b/src/Ocelot/Cache/Middleware/OutputCacheMiddlewareExtensions.cs
index 89e343a0..e28df6f0 100644
--- a/src/Ocelot/Cache/Middleware/OutputCacheMiddlewareExtensions.cs
+++ b/src/Ocelot/Cache/Middleware/OutputCacheMiddlewareExtensions.cs
@@ -1,10 +1,11 @@
using Microsoft.AspNetCore.Builder;
+using Ocelot.Middleware.Pipeline;
namespace Ocelot.Cache.Middleware
{
public static class OutputCacheMiddlewareExtensions
{
- public static IApplicationBuilder UseOutputCacheMiddleware(this IApplicationBuilder builder)
+ public static IOcelotPipelineBuilder UseOutputCacheMiddleware(this IOcelotPipelineBuilder builder)
{
return builder.UseMiddleware();
}
diff --git a/src/Ocelot/Claims/Middleware/ClaimsBuilderMiddleware.cs b/src/Ocelot/Claims/Middleware/ClaimsBuilderMiddleware.cs
index afd2fae2..5161b247 100644
--- a/src/Ocelot/Claims/Middleware/ClaimsBuilderMiddleware.cs
+++ b/src/Ocelot/Claims/Middleware/ClaimsBuilderMiddleware.cs
@@ -1,6 +1,7 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
+using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging;
using Ocelot.Middleware;
@@ -9,34 +10,32 @@ namespace Ocelot.Claims.Middleware
{
public class ClaimsBuilderMiddleware : OcelotMiddleware
{
- private readonly RequestDelegate _next;
+ private readonly OcelotRequestDelegate _next;
private readonly IAddClaimsToRequest _addClaimsToRequest;
private readonly IOcelotLogger _logger;
- public ClaimsBuilderMiddleware(RequestDelegate next,
- IRequestScopedDataRepository requestScopedDataRepository,
+ public ClaimsBuilderMiddleware(OcelotRequestDelegate next,
IOcelotLoggerFactory loggerFactory,
IAddClaimsToRequest addClaimsToRequest)
- : base(requestScopedDataRepository)
{
_next = next;
_addClaimsToRequest = addClaimsToRequest;
_logger = loggerFactory.CreateLogger();
}
- 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");
- var result = _addClaimsToRequest.SetClaimsOnContext(DownstreamRoute.ReRoute.ClaimsToClaims, context);
+ var result = _addClaimsToRequest.SetClaimsOnContext(context.DownstreamReRoute.ClaimsToClaims, context.HttpContext);
if (result.IsError)
{
_logger.LogDebug("error converting claims to other claims, setting pipeline error");
- SetPipelineError(result.Errors);
+ SetPipelineError(context, result.Errors);
return;
}
}
diff --git a/src/Ocelot/Claims/Middleware/ClaimsBuilderMiddlewareExtensions.cs b/src/Ocelot/Claims/Middleware/ClaimsBuilderMiddlewareExtensions.cs
index 099a0add..0790ec7c 100644
--- a/src/Ocelot/Claims/Middleware/ClaimsBuilderMiddlewareExtensions.cs
+++ b/src/Ocelot/Claims/Middleware/ClaimsBuilderMiddlewareExtensions.cs
@@ -1,12 +1,13 @@
using Microsoft.AspNetCore.Builder;
+using Ocelot.Middleware.Pipeline;
namespace Ocelot.Claims.Middleware
{
public static class ClaimsBuilderMiddlewareExtensions
{
- public static IApplicationBuilder UseClaimsBuilderMiddleware(this IApplicationBuilder builder)
+ public static IOcelotPipelineBuilder UseClaimsBuilderMiddleware(this IOcelotPipelineBuilder builder)
{
return builder.UseMiddleware();
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs b/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs
new file mode 100644
index 00000000..bc860424
--- /dev/null
+++ b/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs
@@ -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 _upstreamHttpMethod;
+ private bool _isAuthenticated;
+ private List _claimsToHeaders;
+ private List _claimToClaims;
+ private Dictionary _routeClaimRequirement;
+ private bool _isAuthorised;
+ private List _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 _upstreamHeaderFindAndReplace;
+ private List _downstreamHeaderFindAndReplace;
+ private readonly List _downstreamAddresses;
+ private string _upstreamHost;
+ private string _key;
+
+ public DownstreamReRouteBuilder()
+ {
+ _downstreamAddresses = new List();
+ }
+
+ public DownstreamReRouteBuilder WithDownstreamAddresses(List 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 input)
+ {
+ _upstreamHttpMethod = (input.Count == 0) ? new List() : 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 input)
+ {
+ _claimsToHeaders = input;
+ return this;
+ }
+
+ public DownstreamReRouteBuilder WithClaimsToClaims(List input)
+ {
+ _claimToClaims = input;
+ return this;
+ }
+
+ public DownstreamReRouteBuilder WithRouteClaimsRequirement(Dictionary input)
+ {
+ _routeClaimRequirement = input;
+ return this;
+ }
+
+ public DownstreamReRouteBuilder WithClaimsToQueries(List 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 upstreamHeaderFindAndReplace)
+ {
+ _upstreamHeaderFindAndReplace = upstreamHeaderFindAndReplace;
+ return this;
+ }
+
+ public DownstreamReRouteBuilder WithDownstreamHeaderFindAndReplace(List 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);
+ }
+ }
+}
diff --git a/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs b/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs
index fffd8daf..0d059932 100644
--- a/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs
+++ b/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs
@@ -9,43 +9,26 @@ namespace Ocelot.Configuration.Builder
{
public class ReRouteBuilder
{
- private AuthenticationOptions _authenticationOptions;
- private string _reRouteKey;
- private string _downstreamPathTemplate;
private string _upstreamTemplate;
private UpstreamPathTemplate _upstreamTemplatePattern;
private List _upstreamHttpMethod;
- private bool _isAuthenticated;
- private List _configHeaderExtractorProperties;
- private List _claimToClaims;
- private Dictionary _routeClaimRequirement;
- private bool _isAuthorised;
- private List _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 _upstreamHeaderFindAndReplace;
- private List _downstreamHeaderFindAndReplace;
- private readonly List _downstreamAddresses;
private string _upstreamHost;
+ private List _downstreamReRoutes;
public ReRouteBuilder()
{
- _downstreamAddresses = new List();
+ _downstreamReRoutes = new List();
}
- public ReRouteBuilder WithDownstreamAddresses(List downstreamAddresses)
+ public ReRouteBuilder WithDownstreamReRoute(DownstreamReRoute value)
{
- _downstreamAddresses.AddRange(downstreamAddresses);
+ _downstreamReRoutes.Add(value);
+ return this;
+ }
+
+ public ReRouteBuilder WithDownstreamReRoutes(List value)
+ {
+ _downstreamReRoutes = value;
return this;
}
@@ -55,24 +38,6 @@ namespace Ocelot.Configuration.Builder
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)
{
_upstreamTemplate = input;
@@ -91,158 +56,15 @@ namespace Ocelot.Configuration.Builder
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 input)
- {
- _configHeaderExtractorProperties = input;
- return this;
- }
-
- public ReRouteBuilder WithClaimsToClaims(List input)
- {
- _claimToClaims = input;
- return this;
- }
-
- public ReRouteBuilder WithRouteClaimsRequirement(Dictionary input)
- {
- _routeClaimRequirement = input;
- return this;
- }
-
- public ReRouteBuilder WithClaimsToQueries(List 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 upstreamHeaderFindAndReplace)
- {
- _upstreamHeaderFindAndReplace = upstreamHeaderFindAndReplace;
- return this;
- }
-
- public ReRouteBuilder WithDownstreamHeaderFindAndReplace(List downstreamHeaderFindAndReplace)
- {
- _downstreamHeaderFindAndReplace = downstreamHeaderFindAndReplace;
- return this;
- }
-
-
public ReRoute Build()
{
return new ReRoute(
- new PathTemplate(_downstreamPathTemplate),
+ _downstreamReRoutes,
new PathTemplate(_upstreamTemplate),
_upstreamHttpMethod,
_upstreamTemplatePattern,
- _isAuthenticated,
- _authenticationOptions,
- _configHeaderExtractorProperties,
- _claimToClaims,
- _routeClaimRequirement,
- _isAuthorised,
- _claimToQueries,
- _requestIdHeaderKey,
- _isCached,
- _fileCacheOptions,
- _downstreamScheme,
- _loadBalancer,
- _reRouteKey,
- _useQos,
- _qosOptions,
- _enableRateLimiting,
- _rateLimitOptions,
- _httpHandlerOptions,
- _useServiceDiscovery,
- _serviceName,
- _upstreamHeaderFindAndReplace,
- _downstreamHeaderFindAndReplace,
- _downstreamAddresses,
- _upstreamHost);
+ _upstreamHost
+ );
}
}
}
diff --git a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs
index a0d5a1fe..fbc58020 100644
--- a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs
+++ b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs
@@ -1,19 +1,14 @@
using System;
using System.Collections.Generic;
-using System.Text;
+using System.Linq;
using System.Threading.Tasks;
-using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Ocelot.Cache;
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.File;
-using Ocelot.Configuration.Parser;
using Ocelot.Configuration.Validator;
using Ocelot.DependencyInjection;
-using Ocelot.LoadBalancer;
-using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.Logging;
-using Ocelot.Requester.QoS;
using Ocelot.Responses;
namespace Ocelot.Configuration.Creator
@@ -97,7 +92,16 @@ namespace Ocelot.Configuration.Creator
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);
}
@@ -108,7 +112,48 @@ namespace Ocelot.Configuration.Creator
return new OkResponse(config);
}
- private ReRoute SetUpReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration)
+ public ReRoute SetUpAggregateReRoute(List 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);
@@ -138,7 +183,8 @@ namespace Ocelot.Configuration.Creator
var downstreamAddresses = _downstreamAddressesCreator.Create(fileReRoute);
- var reRoute = new ReRouteBuilder()
+ var reRoute = new DownstreamReRouteBuilder()
+ .WithKey(fileReRoute.Key)
.WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate)
.WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate)
.WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod)
diff --git a/src/Ocelot/Configuration/Creator/IUpstreamTemplatePatternCreator.cs b/src/Ocelot/Configuration/Creator/IUpstreamTemplatePatternCreator.cs
index b40e5e3e..b8303189 100644
--- a/src/Ocelot/Configuration/Creator/IUpstreamTemplatePatternCreator.cs
+++ b/src/Ocelot/Configuration/Creator/IUpstreamTemplatePatternCreator.cs
@@ -5,6 +5,6 @@ namespace Ocelot.Configuration.Creator
{
public interface IUpstreamTemplatePatternCreator
{
- UpstreamPathTemplate Create(FileReRoute reRoute);
+ UpstreamPathTemplate Create(IReRoute reRoute);
}
-}
\ No newline at end of file
+}
diff --git a/src/Ocelot/Configuration/Creator/UpstreamTemplatePatternCreator.cs b/src/Ocelot/Configuration/Creator/UpstreamTemplatePatternCreator.cs
index cc6ebdc7..1700472a 100644
--- a/src/Ocelot/Configuration/Creator/UpstreamTemplatePatternCreator.cs
+++ b/src/Ocelot/Configuration/Creator/UpstreamTemplatePatternCreator.cs
@@ -12,7 +12,7 @@ namespace Ocelot.Configuration.Creator
private const string RegExForwardSlashOnly = "^/$";
private const string RegExForwardSlashAndOnePlaceHolder = "^/.*";
- public UpstreamPathTemplate Create(FileReRoute reRoute)
+ public UpstreamPathTemplate Create(IReRoute reRoute)
{
var upstreamTemplate = reRoute.UpstreamPathTemplate;
@@ -73,4 +73,4 @@ namespace Ocelot.Configuration.Creator
return upstreamTemplate[i] == '{';
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Ocelot/Configuration/DownstreamReRoute.cs b/src/Ocelot/Configuration/DownstreamReRoute.cs
new file mode 100644
index 00000000..9322d5cf
--- /dev/null
+++ b/src/Ocelot/Configuration/DownstreamReRoute.cs
@@ -0,0 +1,91 @@
+using System.Collections.Generic;
+using Ocelot.Values;
+
+namespace Ocelot.Configuration
+{
+ public class DownstreamReRoute
+ {
+ public DownstreamReRoute(
+ string key,
+ PathTemplate upstreamPathTemplate,
+ List upstreamHeadersFindAndReplace,
+ List downstreamHeadersFindAndReplace,
+ List 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 routeClaimsRequirement,
+ List claimsToQueries,
+ List claimsToHeaders,
+ List claimsToClaims,
+ bool isAuthenticated,
+ bool isAuthorised,
+ AuthenticationOptions authenticationOptions,
+ PathTemplate downstreamPathTemplate,
+ string reRouteKey)
+ {
+ Key = key;
+ UpstreamPathTemplate = upstreamPathTemplate;
+ UpstreamHeadersFindAndReplace = upstreamHeadersFindAndReplace ?? new List();
+ DownstreamHeadersFindAndReplace = downstreamHeadersFindAndReplace ?? new List();
+ DownstreamAddresses = downstreamAddresses ?? new List();
+ 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();
+ ClaimsToHeaders = claimsToHeaders ?? new List();
+ ClaimsToClaims = claimsToClaims ?? new List();
+ IsAuthenticated = isAuthenticated;
+ IsAuthorised = isAuthorised;
+ AuthenticationOptions = authenticationOptions;
+ DownstreamPathTemplate = downstreamPathTemplate;
+ ReRouteKey = reRouteKey;
+ }
+
+ public string Key { get; private set; }
+ public PathTemplate UpstreamPathTemplate { get;private set; }
+ public List UpstreamHeadersFindAndReplace {get;private set;}
+ public List DownstreamHeadersFindAndReplace { get; private set; }
+ public List 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 RouteClaimsRequirement { get; private set; }
+ public List ClaimsToQueries { get; private set; }
+ public List ClaimsToHeaders { get; private set; }
+ public List 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; }
+ }
+}
diff --git a/src/Ocelot/Configuration/File/FileAggregateReRoute.cs b/src/Ocelot/Configuration/File/FileAggregateReRoute.cs
new file mode 100644
index 00000000..8c9eabba
--- /dev/null
+++ b/src/Ocelot/Configuration/File/FileAggregateReRoute.cs
@@ -0,0 +1,18 @@
+using System.Collections.Generic;
+
+namespace Ocelot.Configuration.File
+{
+ public class FileAggregateReRoute : IReRoute
+ {
+ public List 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 UpstreamHttpMethod
+ {
+ get { return new List {"Get"}; }
+ }
+ }
+}
diff --git a/src/Ocelot/Configuration/File/FileConfiguration.cs b/src/Ocelot/Configuration/File/FileConfiguration.cs
index 2c4120aa..6d73ce3f 100644
--- a/src/Ocelot/Configuration/File/FileConfiguration.cs
+++ b/src/Ocelot/Configuration/File/FileConfiguration.cs
@@ -8,9 +8,12 @@ namespace Ocelot.Configuration.File
{
ReRoutes = new List();
GlobalConfiguration = new FileGlobalConfiguration();
+ Aggregates = new List();
}
public List ReRoutes { get; set; }
+ // Seperate field for aggregates because this let's you re-use ReRoutes in multiple Aggregates
+ public List Aggregates { get;set; }
public FileGlobalConfiguration GlobalConfiguration { get; set; }
}
}
diff --git a/src/Ocelot/Configuration/File/FileReRoute.cs b/src/Ocelot/Configuration/File/FileReRoute.cs
index 9ca6c7c9..7169e62c 100644
--- a/src/Ocelot/Configuration/File/FileReRoute.cs
+++ b/src/Ocelot/Configuration/File/FileReRoute.cs
@@ -2,7 +2,7 @@
namespace Ocelot.Configuration.File
{
- public class FileReRoute
+ public class FileReRoute : IReRoute
{
public FileReRoute()
{
@@ -36,12 +36,13 @@ namespace Ocelot.Configuration.File
public string ServiceName { get; set; }
public string DownstreamScheme {get;set;}
public FileQoSOptions QoSOptions { get; set; }
- public string LoadBalancer {get;set;}
+ public string LoadBalancer { get;set; }
public FileRateLimitRule RateLimitOptions { get; set; }
public FileAuthenticationOptions AuthenticationOptions { get; set; }
public FileHttpHandlerOptions HttpHandlerOptions { get; set; }
- public bool UseServiceDiscovery {get;set;}
+ public bool UseServiceDiscovery { get;set; }
public List DownstreamHostAndPorts {get;set;}
public string UpstreamHost { get; set; }
+ public string Key { get;set; }
}
}
diff --git a/src/Ocelot/Configuration/File/IReRoute.cs b/src/Ocelot/Configuration/File/IReRoute.cs
new file mode 100644
index 00000000..69128d3a
--- /dev/null
+++ b/src/Ocelot/Configuration/File/IReRoute.cs
@@ -0,0 +1,8 @@
+namespace Ocelot.Configuration.File
+{
+ public interface IReRoute
+ {
+ string UpstreamPathTemplate { get; set; }
+ bool ReRouteIsCaseSensitive { get; set; }
+ }
+}
diff --git a/src/Ocelot/Configuration/ReRoute.cs b/src/Ocelot/Configuration/ReRoute.cs
index 607e971c..47f26291 100644
--- a/src/Ocelot/Configuration/ReRoute.cs
+++ b/src/Ocelot/Configuration/ReRoute.cs
@@ -1,98 +1,30 @@
using System.Collections.Generic;
using System.Net.Http;
using Ocelot.Configuration.Creator;
+using Ocelot.Requester.QoS;
using Ocelot.Values;
namespace Ocelot.Configuration
{
public class ReRoute
{
- public ReRoute(PathTemplate downstreamPathTemplate,
+ public ReRoute(List downstreamReRoute,
PathTemplate upstreamPathTemplate,
List upstreamHttpMethod,
UpstreamPathTemplate upstreamTemplatePattern,
- bool isAuthenticated,
- AuthenticationOptions authenticationOptions,
- List claimsToHeaders,
- List claimsToClaims,
- Dictionary routeClaimsRequirement,
- bool isAuthorised,
- List 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 upstreamHeadersFindAndReplace,
- List downstreamHeadersFindAndReplace,
- List downstreamAddresses,
string upstreamHost)
{
UpstreamHost = upstreamHost;
- DownstreamHeadersFindAndReplace = downstreamHeadersFindAndReplace ?? new List();
- UpstreamHeadersFindAndReplace = upstreamHeadersFindAndReplace ?? new List();
- ServiceName = serviceName;
- UseServiceDiscovery = useServiceDiscovery;
- ReRouteKey = reRouteKey;
- LoadBalancer = loadBalancer;
- DownstreamAddresses = downstreamAddresses ?? new List();
- DownstreamPathTemplate = downstreamPathTemplate;
+ DownstreamReRoute = downstreamReRoute;
UpstreamPathTemplate = upstreamPathTemplate;
UpstreamHttpMethod = upstreamHttpMethod;
UpstreamTemplatePattern = upstreamTemplatePattern;
- IsAuthenticated = isAuthenticated;
- AuthenticationOptions = authenticationOptions;
- RouteClaimsRequirement = routeClaimsRequirement;
- IsAuthorised = isAuthorised;
- RequestIdKey = requestIdKey;
- IsCached = isCached;
- CacheOptions = cacheOptions;
- ClaimsToQueries = claimsToQueries ?? new List();
- ClaimsToClaims = claimsToClaims ?? new List();
- ClaimsToHeaders = claimsToHeaders ?? new List();
- 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 UpstreamPathTemplate UpstreamTemplatePattern { get; private set; }
public List UpstreamHttpMethod { get; private set; }
- public bool IsAuthenticated { get; private set; }
- public bool IsAuthorised { get; private set; }
- public AuthenticationOptions AuthenticationOptions { get; private set; }
- public List ClaimsToQueries { get; private set; }
- public List ClaimsToHeaders { get; private set; }
- public List ClaimsToClaims { get; private set; }
- public Dictionary 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 UpstreamHeadersFindAndReplace {get;private set;}
- public List DownstreamHeadersFindAndReplace {get;private set;}
- public List DownstreamAddresses {get;private set;}
public string UpstreamHost { get; private set; }
+ public List DownstreamReRoute { get; private set; }
}
}
diff --git a/src/Ocelot/Configuration/Repository/ConsulFileConfigurationPoller.cs b/src/Ocelot/Configuration/Repository/ConsulFileConfigurationPoller.cs
index 8274af2f..7e78b3aa 100644
--- a/src/Ocelot/Configuration/Repository/ConsulFileConfigurationPoller.cs
+++ b/src/Ocelot/Configuration/Repository/ConsulFileConfigurationPoller.cs
@@ -11,11 +11,11 @@ namespace Ocelot.Configuration.Repository
{
public class ConsulFileConfigurationPoller : IDisposable
{
- private IOcelotLogger _logger;
- private IFileConfigurationRepository _repo;
- private IFileConfigurationSetter _setter;
+ private readonly IOcelotLogger _logger;
+ private readonly IFileConfigurationRepository _repo;
+ private readonly IFileConfigurationSetter _setter;
private string _previousAsJson;
- private Timer _timer;
+ private readonly Timer _timer;
private bool _polling;
public ConsulFileConfigurationPoller(IOcelotLoggerFactory factory, IFileConfigurationRepository repo, IFileConfigurationSetter setter)
@@ -77,4 +77,4 @@ namespace Ocelot.Configuration.Repository
_timer.Dispose();
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs b/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs
index 0a150a02..58da7c4f 100644
--- a/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs
+++ b/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs
@@ -19,6 +19,29 @@ namespace Ocelot.Configuration.Validator
RuleForEach(configuration => configuration.ReRoutes)
.Must((config, reRoute) => IsNotDuplicateIn(reRoute, config.ReRoutes))
.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 reRoutes)
+ {
+ var reRoutesForAggregate = reRoutes.Where(r => fileAggregateReRoute.ReRouteKeys.Contains(r.Key));
+
+ return reRoutesForAggregate.Count() == fileAggregateReRoute.ReRouteKeys.Count;
}
public async Task> IsValid(FileConfiguration configuration)
@@ -37,10 +60,21 @@ namespace Ocelot.Configuration.Validator
return new OkResponse(result);
}
- private static bool IsNotDuplicateIn(FileReRoute reRoute, List reRoutes)
+ private static bool DoesNotContainReRoutesWithSpecificRequestIdKeys(FileAggregateReRoute fileAggregateReRoute,
+ List 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 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)
{
@@ -62,5 +96,27 @@ namespace Ocelot.Configuration.Validator
return true;
}
+
+ private static bool IsNotDuplicateIn(FileReRoute reRoute,
+ List 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 aggregateReRoutes)
+ {
+ var matchingReRoutes = aggregateReRoutes
+ .Where(r => r.UpstreamPathTemplate == reRoute.UpstreamPathTemplate
+ && r.UpstreamHost == reRoute.UpstreamHost)
+ .ToList();
+
+ return matchingReRoutes.Count <= 1;
+ }
}
}
diff --git a/src/Ocelot/DependencyInjection/OcelotBuilder.cs b/src/Ocelot/DependencyInjection/OcelotBuilder.cs
index d51b8a8d..f4192713 100644
--- a/src/Ocelot/DependencyInjection/OcelotBuilder.cs
+++ b/src/Ocelot/DependencyInjection/OcelotBuilder.cs
@@ -1,4 +1,6 @@
+using Butterfly.Client.Tracing;
using Microsoft.Extensions.Options;
+using Ocelot.Middleware.Multiplexer;
namespace Ocelot.DependencyInjection
{
@@ -30,7 +32,6 @@ namespace Ocelot.DependencyInjection
using Ocelot.Middleware;
using Ocelot.QueryStrings;
using Ocelot.RateLimit;
- using Ocelot.Request.Builder;
using Ocelot.Request.Mapper;
using Ocelot.Requester;
using Ocelot.Requester.QoS;
@@ -114,7 +115,6 @@ namespace Ocelot.DependencyInjection
_services.TryAddSingleton();
_services.TryAddSingleton();
_services.TryAddSingleton();
- _services.TryAddSingleton();
_services.TryAddSingleton();
_services.TryAddSingleton();
_services.TryAddSingleton();
@@ -126,7 +126,7 @@ namespace Ocelot.DependencyInjection
// see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc
// could maybe use a scoped data repository
- _services.TryAddSingleton();
+ _services.TryAddSingleton();
_services.TryAddSingleton();
_services.AddMemoryCache();
_services.TryAddSingleton();
@@ -147,8 +147,14 @@ namespace Ocelot.DependencyInjection
//these get picked out later and added to http request
_provider = new DelegatingHandlerHandlerProvider();
- _services.TryAddSingleton(_provider);
- _services.AddTransient();
+ _services.TryAddSingleton(_provider);
+ _services.TryAddSingleton();
+ _services.TryAddSingleton();
+ _services.AddSingleton();
+
+ // We add this here so that we can always inject something into the factory for IoC..
+ _services.AddSingleton();
+
}
public IOcelotAdministrationBuilder AddAdministration(string path, string secret)
@@ -191,7 +197,8 @@ namespace Ocelot.DependencyInjection
public IOcelotBuilder AddOpenTracing(Action settings)
{
- _services.AddTransient();
+ // Earlier we add FakeServiceTracer and need to remove it here before we add butterfly
+ _services.RemoveAll();
_services.AddButterfly(settings);
return this;
}
diff --git a/src/Ocelot/DownstreamRouteFinder/DownstreamRoute.cs b/src/Ocelot/DownstreamRouteFinder/DownstreamRoute.cs
index f2e219cb..ed04daef 100644
--- a/src/Ocelot/DownstreamRouteFinder/DownstreamRoute.cs
+++ b/src/Ocelot/DownstreamRouteFinder/DownstreamRoute.cs
@@ -13,6 +13,5 @@ namespace Ocelot.DownstreamRouteFinder
}
public List TemplatePlaceholderNameAndValues { get; private set; }
public ReRoute ReRoute { get; private set; }
- public object UpstreamHeadersFindAndReplace {get;private set;}
}
}
\ No newline at end of file
diff --git a/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddleware.cs b/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddleware.cs
index 24765fb4..4b467b95 100644
--- a/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddleware.cs
+++ b/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddleware.cs
@@ -1,71 +1,71 @@
-using System.Linq;
-using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
-using Microsoft.AspNetCore.Http;
+using Ocelot.Configuration;
using Ocelot.Configuration.Provider;
using Ocelot.DownstreamRouteFinder.Finder;
using Ocelot.Infrastructure.Extensions;
-using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging;
using Ocelot.Middleware;
+using Ocelot.Middleware.Multiplexer;
namespace Ocelot.DownstreamRouteFinder.Middleware
{
public class DownstreamRouteFinderMiddleware : OcelotMiddleware
{
- private readonly RequestDelegate _next;
+ private readonly OcelotRequestDelegate _next;
private readonly IDownstreamRouteFinder _downstreamRouteFinder;
private readonly IOcelotLogger _logger;
private readonly IOcelotConfigurationProvider _configProvider;
+ private readonly IMultiplexer _multiplexer;
- public DownstreamRouteFinderMiddleware(RequestDelegate next,
+ public DownstreamRouteFinderMiddleware(OcelotRequestDelegate next,
IOcelotLoggerFactory loggerFactory,
- IDownstreamRouteFinder downstreamRouteFinder,
- IRequestScopedDataRepository requestScopedDataRepository,
- IOcelotConfigurationProvider configProvider)
- :base(requestScopedDataRepository)
+ IDownstreamRouteFinder downstreamRouteFinder,
+ IOcelotConfigurationProvider configProvider,
+ IMultiplexer multiplexer)
{
_configProvider = configProvider;
+ _multiplexer = multiplexer;
_next = next;
_downstreamRouteFinder = downstreamRouteFinder;
_logger = loggerFactory.CreateLogger();
}
- 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();
-
- if(configuration.IsError)
+ var configuration = await _configProvider.Get();
+
+ if (configuration.IsError)
{
_logger.LogError($"{MiddlewareName} setting pipeline errors. IOcelotConfigurationProvider returned {configuration.Errors.ToErrorString()}");
- SetPipelineError(configuration.Errors);
+ SetPipelineError(context, configuration.Errors);
return;
}
- SetServiceProviderConfigurationForThisRequest(configuration.Data.ServiceProviderConfiguration);
+ context.ServiceProviderConfiguration = configuration.Data.ServiceProviderConfiguration;
_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)
{
_logger.LogError($"{MiddlewareName} setting pipeline errors. IDownstreamRouteFinder returned {downstreamRoute.Errors.ToErrorString()}");
- SetPipelineError(downstreamRoute.Errors);
+ SetPipelineError(context, downstreamRoute.Errors);
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);
}
}
}
diff --git a/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddlewareExtensions.cs b/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddlewareExtensions.cs
index 81d2dd2d..13dacac8 100644
--- a/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddlewareExtensions.cs
+++ b/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddlewareExtensions.cs
@@ -1,12 +1,13 @@
using Microsoft.AspNetCore.Builder;
+using Ocelot.Middleware.Pipeline;
namespace Ocelot.DownstreamRouteFinder.Middleware
{
public static class DownstreamRouteFinderMiddlewareExtensions
{
- public static IApplicationBuilder UseDownstreamRouteFinderMiddleware(this IApplicationBuilder builder)
+ public static IOcelotPipelineBuilder UseDownstreamRouteFinderMiddleware(this IOcelotPipelineBuilder builder)
{
return builder.UseMiddleware();
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs
index 01a388c5..5b5e8346 100644
--- a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs
+++ b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs
@@ -5,22 +5,21 @@ using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging;
using Ocelot.Middleware;
using System;
+using Ocelot.DownstreamRouteFinder.Middleware;
namespace Ocelot.DownstreamUrlCreator.Middleware
{
public class DownstreamUrlCreatorMiddleware : OcelotMiddleware
{
- private readonly RequestDelegate _next;
+ private readonly OcelotRequestDelegate _next;
private readonly IDownstreamPathPlaceholderReplacer _replacer;
private readonly IOcelotLogger _logger;
private readonly IUrlBuilder _urlBuilder;
- public DownstreamUrlCreatorMiddleware(RequestDelegate next,
+ public DownstreamUrlCreatorMiddleware(OcelotRequestDelegate next,
IOcelotLoggerFactory loggerFactory,
IDownstreamPathPlaceholderReplacer replacer,
- IRequestScopedDataRepository requestScopedDataRepository,
IUrlBuilder urlBuilder)
- :base(requestScopedDataRepository)
{
_next = next;
_replacer = replacer;
@@ -28,30 +27,30 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
_logger = loggerFactory.CreateLogger();
}
- public async Task Invoke(HttpContext context)
+ public async Task Invoke(DownstreamContext context)
{
var dsPath = _replacer
- .Replace(DownstreamRoute.ReRoute.DownstreamPathTemplate, DownstreamRoute.TemplatePlaceholderNameAndValues);
+ .Replace(context.DownstreamReRoute.DownstreamPathTemplate, context.TemplatePlaceholderNameAndValues);
if (dsPath.IsError)
{
_logger.LogDebug("IDownstreamPathPlaceholderReplacer returned an error, setting pipeline error");
- SetPipelineError(dsPath.Errors);
+ SetPipelineError(context, dsPath.Errors);
return;
}
- var uriBuilder = new UriBuilder(DownstreamRequest.RequestUri)
+ var uriBuilder = new UriBuilder(context.DownstreamRequest.RequestUri)
{
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);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddlewareExtensions.cs b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddlewareExtensions.cs
index 238bc7ef..671636c5 100644
--- a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddlewareExtensions.cs
+++ b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddlewareExtensions.cs
@@ -1,12 +1,13 @@
using Microsoft.AspNetCore.Builder;
+using Ocelot.Middleware.Pipeline;
namespace Ocelot.DownstreamUrlCreator.Middleware
{
public static class DownstreamUrlCreatorMiddlewareExtensions
{
- public static IApplicationBuilder UseDownstreamUrlCreatorMiddleware(this IApplicationBuilder builder)
+ public static IOcelotPipelineBuilder UseDownstreamUrlCreatorMiddleware(this IOcelotPipelineBuilder builder)
{
return builder.UseMiddleware();
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddleware.cs b/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddleware.cs
index ee1f0de5..9b49c84a 100644
--- a/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddleware.cs
+++ b/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddleware.cs
@@ -4,6 +4,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
using Ocelot.Configuration.Provider;
+using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.Infrastructure.Extensions;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging;
@@ -16,24 +17,23 @@ namespace Ocelot.Errors.Middleware
///
public class ExceptionHandlerMiddleware : OcelotMiddleware
{
- private readonly RequestDelegate _next;
+ private readonly OcelotRequestDelegate _next;
private readonly IOcelotLogger _logger;
- private readonly IRequestScopedDataRepository _requestScopedDataRepository;
- private readonly IOcelotConfigurationProvider _configProvider;
+ private readonly IOcelotConfigurationProvider _provider;
+ private readonly IRequestScopedDataRepository _repo;
- public ExceptionHandlerMiddleware(RequestDelegate next,
+ public ExceptionHandlerMiddleware(OcelotRequestDelegate next,
IOcelotLoggerFactory loggerFactory,
- IRequestScopedDataRepository requestScopedDataRepository,
- IOcelotConfigurationProvider configProvider)
- :base(requestScopedDataRepository)
+ IOcelotConfigurationProvider provider,
+ IRequestScopedDataRepository repo)
{
- _configProvider = configProvider;
+ _provider = provider;
+ _repo = repo;
_next = next;
- _requestScopedDataRepository = requestScopedDataRepository;
_logger = loggerFactory.CreateLogger();
}
- public async Task Invoke(HttpContext context)
+ public async Task Invoke(DownstreamContext context)
{
try
{
@@ -57,12 +57,12 @@ namespace Ocelot.Errors.Middleware
_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...
//should this basically be immutable per request...i guess it should!
//first thing is get config
- var configuration = await _configProvider.Get();
+ var configuration = await _provider.Get();
//if error throw to catch below..
if(configuration.IsError)
@@ -74,22 +74,23 @@ namespace Ocelot.Errors.Middleware
var key = configuration.Data.RequestId;
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();
- _requestScopedDataRepository.Add("RequestId", context.TraceIdentifier);
- }
- }
-
- private void SetInternalServerErrorOnResponse(HttpContext context)
- {
- if (!context.Response.HasStarted)
- {
- context.Response.StatusCode = 500;
+ //todo fix looking in both places
+ context.HttpContext.TraceIdentifier = upstreamRequestIds.First();
+ _repo.Add("RequestId", context.HttpContext.TraceIdentifier);
}
}
- 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 =
$"Exception caught in global error handler, exception message: {e.Message}, exception stack: {e.StackTrace}";
@@ -99,7 +100,7 @@ namespace Ocelot.Errors.Middleware
message =
$"{message}, inner exception message {e.InnerException.Message}, inner exception stack {e.InnerException.StackTrace}";
}
- return $"{message} RequestId: {context.TraceIdentifier}";
+ return $"{message} RequestId: {context.HttpContext.TraceIdentifier}";
}
}
}
diff --git a/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddlewareExtensions.cs b/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddlewareExtensions.cs
index 14731eb2..5d8874b0 100644
--- a/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddlewareExtensions.cs
+++ b/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddlewareExtensions.cs
@@ -1,10 +1,11 @@
using Microsoft.AspNetCore.Builder;
+using Ocelot.Middleware.Pipeline;
namespace Ocelot.Errors.Middleware
{
public static class ExceptionHandlerMiddlewareExtensions
{
- public static IApplicationBuilder UseExceptionHandlerMiddleware(this IApplicationBuilder builder)
+ public static IOcelotPipelineBuilder UseExceptionHandlerMiddleware(this IOcelotPipelineBuilder builder)
{
return builder.UseMiddleware();
}
diff --git a/src/Ocelot/Headers/Middleware/HttpHeadersTransformationMiddleware.cs b/src/Ocelot/Headers/Middleware/HttpHeadersTransformationMiddleware.cs
index b86cc4d2..f4281a23 100644
--- a/src/Ocelot/Headers/Middleware/HttpHeadersTransformationMiddleware.cs
+++ b/src/Ocelot/Headers/Middleware/HttpHeadersTransformationMiddleware.cs
@@ -1,5 +1,6 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
+using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging;
using Ocelot.Middleware;
@@ -8,17 +9,15 @@ namespace Ocelot.Headers.Middleware
{
public class HttpHeadersTransformationMiddleware : OcelotMiddleware
{
- private readonly RequestDelegate _next;
+ private readonly OcelotRequestDelegate _next;
private readonly IOcelotLogger _logger;
private readonly IHttpContextRequestHeaderReplacer _preReplacer;
private readonly IHttpResponseHeaderReplacer _postReplacer;
- public HttpHeadersTransformationMiddleware(RequestDelegate next,
+ public HttpHeadersTransformationMiddleware(OcelotRequestDelegate next,
IOcelotLoggerFactory loggerFactory,
- IRequestScopedDataRepository requestScopedDataRepository,
IHttpContextRequestHeaderReplacer preReplacer,
IHttpResponseHeaderReplacer postReplacer)
- : base(requestScopedDataRepository)
{
_next = next;
_postReplacer = postReplacer;
@@ -26,17 +25,18 @@ namespace Ocelot.Headers.Middleware
_logger = loggerFactory.CreateLogger();
}
- 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);
- var postFAndRs = this.DownstreamRoute.ReRoute.DownstreamHeadersFindAndReplace;
+ var postFAndRs = context.DownstreamReRoute.DownstreamHeadersFindAndReplace;
- _postReplacer.Replace(HttpResponseMessage, postFAndRs, DownstreamRequest);
+ _postReplacer.Replace(context.DownstreamResponse, postFAndRs, context.DownstreamRequest);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Ocelot/Headers/Middleware/HttpHeadersTransformationMiddlewareExtensions.cs b/src/Ocelot/Headers/Middleware/HttpHeadersTransformationMiddlewareExtensions.cs
index ce920b1a..46bb84dc 100644
--- a/src/Ocelot/Headers/Middleware/HttpHeadersTransformationMiddlewareExtensions.cs
+++ b/src/Ocelot/Headers/Middleware/HttpHeadersTransformationMiddlewareExtensions.cs
@@ -1,12 +1,13 @@
using Microsoft.AspNetCore.Builder;
+using Ocelot.Middleware.Pipeline;
namespace Ocelot.Headers.Middleware
{
public static class HttpHeadersTransformationMiddlewareExtensions
{
- public static IApplicationBuilder UseHttpHeadersTransformationMiddleware(this IApplicationBuilder builder)
+ public static IOcelotPipelineBuilder UseHttpHeadersTransformationMiddleware(this IOcelotPipelineBuilder builder)
{
return builder.UseMiddleware();
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Ocelot/Headers/Middleware/HttpRequestHeadersBuilderMiddleware.cs b/src/Ocelot/Headers/Middleware/HttpRequestHeadersBuilderMiddleware.cs
index 35d2fe5a..6e8dc31e 100644
--- a/src/Ocelot/Headers/Middleware/HttpRequestHeadersBuilderMiddleware.cs
+++ b/src/Ocelot/Headers/Middleware/HttpRequestHeadersBuilderMiddleware.cs
@@ -1,6 +1,7 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
+using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging;
using Ocelot.Middleware;
@@ -9,34 +10,32 @@ namespace Ocelot.Headers.Middleware
{
public class HttpRequestHeadersBuilderMiddleware : OcelotMiddleware
{
- private readonly RequestDelegate _next;
+ private readonly OcelotRequestDelegate _next;
private readonly IAddHeadersToRequest _addHeadersToRequest;
private readonly IOcelotLogger _logger;
- public HttpRequestHeadersBuilderMiddleware(RequestDelegate next,
+ public HttpRequestHeadersBuilderMiddleware(OcelotRequestDelegate next,
IOcelotLoggerFactory loggerFactory,
- IRequestScopedDataRepository requestScopedDataRepository,
IAddHeadersToRequest addHeadersToRequest)
- : base(requestScopedDataRepository)
{
_next = next;
_addHeadersToRequest = addHeadersToRequest;
_logger = loggerFactory.CreateLogger();
}
- 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)
{
_logger.LogDebug("Error setting headers on context, setting pipeline error");
- SetPipelineError(response.Errors);
+ SetPipelineError(context, response.Errors);
return;
}
diff --git a/src/Ocelot/Headers/Middleware/HttpRequestHeadersBuilderMiddlewareExtensions.cs b/src/Ocelot/Headers/Middleware/HttpRequestHeadersBuilderMiddlewareExtensions.cs
index 2a8c50fa..69f23860 100644
--- a/src/Ocelot/Headers/Middleware/HttpRequestHeadersBuilderMiddlewareExtensions.cs
+++ b/src/Ocelot/Headers/Middleware/HttpRequestHeadersBuilderMiddlewareExtensions.cs
@@ -1,12 +1,13 @@
using Microsoft.AspNetCore.Builder;
+using Ocelot.Middleware.Pipeline;
namespace Ocelot.Headers.Middleware
{
public static class HttpRequestHeadersBuilderMiddlewareExtensions
{
- public static IApplicationBuilder UseHttpRequestHeadersBuilderMiddleware(this IApplicationBuilder builder)
+ public static IOcelotPipelineBuilder UseHttpRequestHeadersBuilderMiddleware(this IOcelotPipelineBuilder builder)
{
return builder.UseMiddleware();
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerFactory.cs b/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerFactory.cs
index 8a9803fb..f02230ec 100644
--- a/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerFactory.cs
+++ b/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerFactory.cs
@@ -5,6 +5,6 @@ namespace Ocelot.LoadBalancer.LoadBalancers
{
public interface ILoadBalancerFactory
{
- Task Get(ReRoute reRoute, ServiceProviderConfiguration config);
+ Task Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config);
}
}
\ No newline at end of file
diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerHouse.cs b/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerHouse.cs
index a4711fbe..d9d051c2 100644
--- a/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerHouse.cs
+++ b/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerHouse.cs
@@ -6,6 +6,6 @@ namespace Ocelot.LoadBalancer.LoadBalancers
{
public interface ILoadBalancerHouse
{
- Task> Get(ReRoute reRoute, ServiceProviderConfiguration config);
+ Task> Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config);
}
}
\ No newline at end of file
diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs b/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs
index 3a583b8c..29d84ba4 100644
--- a/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs
+++ b/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs
@@ -12,7 +12,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers
_serviceProviderFactory = serviceProviderFactory;
}
- public async Task Get(ReRoute reRoute, ServiceProviderConfiguration config)
+ public async Task Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config)
{
var serviceProvider = _serviceProviderFactory.Get(config, reRoute);
diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerHouse.cs b/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerHouse.cs
index 7fc1ae05..3d8059b9 100644
--- a/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerHouse.cs
+++ b/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerHouse.cs
@@ -18,7 +18,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers
_loadBalancers = new ConcurrentDictionary();
}
- public async Task> Get(ReRoute reRoute, ServiceProviderConfiguration config)
+ public async Task> Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config)
{
try
{
diff --git a/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs b/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs
index aa37196f..8c2e963a 100644
--- a/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs
+++ b/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs
@@ -1,6 +1,7 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
+using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.Infrastructure.RequestData;
using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.Logging;
@@ -11,28 +12,26 @@ namespace Ocelot.LoadBalancer.Middleware
{
public class LoadBalancingMiddleware : OcelotMiddleware
{
- private readonly RequestDelegate _next;
+ private readonly OcelotRequestDelegate _next;
private readonly IOcelotLogger _logger;
private readonly ILoadBalancerHouse _loadBalancerHouse;
- public LoadBalancingMiddleware(RequestDelegate next,
+ public LoadBalancingMiddleware(OcelotRequestDelegate next,
IOcelotLoggerFactory loggerFactory,
- IRequestScopedDataRepository requestScopedDataRepository,
ILoadBalancerHouse loadBalancerHouse)
- : base(requestScopedDataRepository)
{
_next = next;
- _logger = loggerFactory.CreateLogger();
+ _logger = loggerFactory.CreateLogger();
_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)
{
_logger.LogDebug("there was an error retriving the loadbalancer, setting pipeline error");
- SetPipelineError(loadBalancer.Errors);
+ SetPipelineError(context, loadBalancer.Errors);
return;
}
@@ -40,11 +39,11 @@ namespace Ocelot.LoadBalancer.Middleware
if(hostAndPort.IsError)
{
_logger.LogDebug("there was an error leasing the loadbalancer, setting pipeline error");
- SetPipelineError(hostAndPort.Errors);
+ SetPipelineError(context, hostAndPort.Errors);
return;
}
- var uriBuilder = new UriBuilder(DownstreamRequest.RequestUri);
+ var uriBuilder = new UriBuilder(context.DownstreamRequest.RequestUri);
uriBuilder.Host = hostAndPort.Data.DownstreamHost;
@@ -53,7 +52,7 @@ namespace Ocelot.LoadBalancer.Middleware
uriBuilder.Port = hostAndPort.Data.DownstreamPort;
}
- DownstreamRequest.RequestUri = uriBuilder.Uri;
+ context.DownstreamRequest.RequestUri = uriBuilder.Uri;
try
{
diff --git a/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddlewareExtensions.cs b/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddlewareExtensions.cs
index db026396..a2da1060 100644
--- a/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddlewareExtensions.cs
+++ b/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddlewareExtensions.cs
@@ -1,12 +1,13 @@
using Microsoft.AspNetCore.Builder;
+using Ocelot.Middleware.Pipeline;
namespace Ocelot.LoadBalancer.Middleware
{
public static class LoadBalancingMiddlewareExtensions
{
- public static IApplicationBuilder UseLoadBalancingMiddleware(this IApplicationBuilder builder)
+ public static IOcelotPipelineBuilder UseLoadBalancingMiddleware(this IOcelotPipelineBuilder builder)
{
return builder.UseMiddleware();
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Ocelot/Middleware/DownstreamContext.cs b/src/Ocelot/Middleware/DownstreamContext.cs
new file mode 100644
index 00000000..84ebc77a
--- /dev/null
+++ b/src/Ocelot/Middleware/DownstreamContext.cs
@@ -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();
+ }
+
+ public List 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 Errors { get;set; }
+ //public string RequestId {get;set;}
+ //public string PreviousRequestId {get;set;}
+ public bool IsError => Errors.Count > 0;
+ }
+}
diff --git a/src/Ocelot/Middleware/Multiplexer/IMultiplexer.cs b/src/Ocelot/Middleware/Multiplexer/IMultiplexer.cs
new file mode 100644
index 00000000..f3a2975d
--- /dev/null
+++ b/src/Ocelot/Middleware/Multiplexer/IMultiplexer.cs
@@ -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);
+ }
+}
diff --git a/src/Ocelot/Middleware/Multiplexer/IResponseAggregator.cs b/src/Ocelot/Middleware/Multiplexer/IResponseAggregator.cs
new file mode 100644
index 00000000..b85c2cc8
--- /dev/null
+++ b/src/Ocelot/Middleware/Multiplexer/IResponseAggregator.cs
@@ -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 downstreamContexts);
+ }
+}
diff --git a/src/Ocelot/Middleware/Multiplexer/Multiplexer.cs b/src/Ocelot/Middleware/Multiplexer/Multiplexer.cs
new file mode 100644
index 00000000..c4ebb08b
--- /dev/null
+++ b/src/Ocelot/Middleware/Multiplexer/Multiplexer.cs
@@ -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[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();
+
+ foreach (var task in tasks)
+ {
+ var finished = await task;
+ downstreamContexts.Add(finished);
+ }
+
+ await _aggregator.Aggregate(reRoute, context, downstreamContexts);
+ }
+
+ private async Task Fire(DownstreamContext context, OcelotRequestDelegate next)
+ {
+ await next.Invoke(context);
+ return context;
+ }
+ }
+}
diff --git a/src/Ocelot/Middleware/Multiplexer/SimpleJsonResponseAggregator.cs b/src/Ocelot/Middleware/Multiplexer/SimpleJsonResponseAggregator.cs
new file mode 100644
index 00000000..3d9a997d
--- /dev/null
+++ b/src/Ocelot/Middleware/Multiplexer/SimpleJsonResponseAggregator.cs
@@ -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 downstreamContexts)
+ {
+ if (reRoute.DownstreamReRoute.Count > 1)
+ {
+ await MapAggregtes(originalContext, downstreamContexts);
+ }
+ else
+ {
+ MapNotAggregate(originalContext, downstreamContexts);
+ }
+ }
+
+ private async Task MapAggregtes(DownstreamContext originalContext, List downstreamContexts)
+ {
+ await MapAggregateContent(originalContext, downstreamContexts);
+ }
+
+ private static async Task MapAggregateContent(DownstreamContext originalContext, List 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 downstreamContexts, int i)
+ {
+ originalContext.Errors.AddRange(downstreamContexts[i].Errors);
+ originalContext.DownstreamResponse = downstreamContexts[i].DownstreamResponse;
+ }
+
+ private void MapNotAggregate(DownstreamContext originalContext, List 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;
+ }
+ }
+}
diff --git a/src/Ocelot/Middleware/OcelotMiddleware.cs b/src/Ocelot/Middleware/OcelotMiddleware.cs
index 8b1eae2e..bf49fd03 100644
--- a/src/Ocelot/Middleware/OcelotMiddleware.cs
+++ b/src/Ocelot/Middleware/OcelotMiddleware.cs
@@ -1,67 +1,20 @@
using System.Collections.Generic;
-using System.Net.Http;
-using Ocelot.Configuration;
-using Ocelot.DownstreamRouteFinder;
using Ocelot.Errors;
-using Ocelot.Infrastructure.RequestData;
namespace Ocelot.Middleware
{
public abstract class OcelotMiddleware
{
- private readonly IRequestScopedDataRepository _requestScopedDataRepository;
-
- protected OcelotMiddleware(IRequestScopedDataRepository requestScopedDataRepository)
+ protected OcelotMiddleware()
{
- _requestScopedDataRepository = requestScopedDataRepository;
MiddlewareName = this.GetType().Name;
}
public string MiddlewareName { get; }
- public bool PipelineError => _requestScopedDataRepository.Get("OcelotMiddlewareError").Data;
-
- public List PipelineErrors => _requestScopedDataRepository.Get>("OcelotMiddlewareErrors").Data;
-
- public DownstreamRoute DownstreamRoute => _requestScopedDataRepository.Get("DownstreamRoute").Data;
-
- public Request.Request Request => _requestScopedDataRepository.Get("Request").Data;
-
- public HttpRequestMessage DownstreamRequest => _requestScopedDataRepository.Get("DownstreamRequest").Data;
-
- public HttpResponseMessage HttpResponseMessage => _requestScopedDataRepository.Get("HttpResponseMessage").Data;
-
- public ServiceProviderConfiguration ServiceProviderConfiguration => _requestScopedDataRepository.Get("ServiceProviderConfiguration").Data;
-
- public void SetDownstreamRouteForThisRequest(DownstreamRoute downstreamRoute)
+ public void SetPipelineError(DownstreamContext context, List errors)
{
- _requestScopedDataRepository.Add("DownstreamRoute", downstreamRoute);
- }
-
- 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 errors)
- {
- _requestScopedDataRepository.Add("OcelotMiddlewareError", true);
- _requestScopedDataRepository.Add("OcelotMiddlewareErrors", errors);
+ context.Errors = errors;
}
}
}
diff --git a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs
index 249dceb5..541a8e93 100644
--- a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs
+++ b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs
@@ -3,7 +3,6 @@
using System;
using System.Linq;
using System.Threading.Tasks;
- using Authorisation.Middleware;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
@@ -15,24 +14,11 @@
using Ocelot.Configuration.Provider;
using Ocelot.Configuration.Repository;
using Ocelot.Configuration.Setter;
- using Ocelot.LoadBalancer.Middleware;
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.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.Infrastructure;
+ using Ocelot.Middleware.Pipeline;
public static class OcelotMiddlewareExtensions
{
@@ -43,7 +29,7 @@
///
public static async Task UseOcelot(this IApplicationBuilder builder)
{
- await builder.UseOcelot(new OcelotMiddlewareConfiguration());
+ await builder.UseOcelot(new OcelotPipelineConfiguration());
return builder;
}
@@ -52,9 +38,9 @@
/// Registers Ocelot with a combination of default middlewares and optional middlewares in the configuration
///
///
- ///
+ ///
///
- public static async Task UseOcelot(this IApplicationBuilder builder, OcelotMiddlewareConfiguration middlewareConfiguration)
+ public static async Task UseOcelot(this IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration)
{
var configuration = await CreateConfiguration(builder);
@@ -67,91 +53,21 @@
ConfigureDiagnosticListener(builder);
- // 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();
+ var pipelineBuilder = new OcelotPipelineBuilder(builder.ApplicationServices);
- // Allow the user to respond with absolutely anything they want.
- builder.UseIfNotNull(middlewareConfiguration.PreErrorResponderMiddleware);
+ pipelineBuilder.BuildOcelotPipeline(pipelineConfiguration);
- // This is registered first so it can catch any errors and issue an appropriate response
- builder.UseResponderMiddleware();
+ var firstDelegate = pipelineBuilder.Build();
- // Then we get the downstream route information
- builder.UseDownstreamRouteFinderMiddleware();
+ //inject first delegate into first piece of asp.net middleware..maybe not like this
+ //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.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.Use(async (context, task) =>
{
- builder.UseAuthenticationMiddleware();
- }
- 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();
+ var downstreamContext = new DownstreamContext(context);
+ await firstDelegate.Invoke(downstreamContext);
+ });
return builder;
}
@@ -257,23 +173,34 @@
var ocelotConfigurationRepository =
(IOcelotConfigurationRepository) builder.ApplicationServices.GetService(
typeof(IOcelotConfigurationRepository));
+
var ocelotConfigurationCreator =
(IOcelotConfigurationCreator) builder.ApplicationServices.GetService(
typeof(IOcelotConfigurationCreator));
var fileConfigFromConsul = await consulFileConfigRepo.Get();
+
if (fileConfigFromConsul.Data == null)
{
config = await setter.Set(fileConfig.Value);
+ var hack = builder.ApplicationServices.GetService(typeof(ConsulFileConfigurationPoller));
}
else
{
var ocelotConfig = await ocelotConfigurationCreator.Create(fileConfigFromConsul.Data);
+
if(ocelotConfig.IsError)
{
return new ErrorResponse(ocelotConfig.Errors);
}
+
config = await ocelotConfigurationRepository.AddOrReplace(ocelotConfig.Data);
+
+ if (config.IsError)
+ {
+ return new ErrorResponse(config.Errors);
+ }
+
//todo - this starts the poller if it has been registered...please this is so bad.
var hack = builder.ApplicationServices.GetService(typeof(ConsulFileConfigurationPoller));
}
diff --git a/src/Ocelot/Middleware/OcelotMiddlewareConfiguration.cs b/src/Ocelot/Middleware/OcelotPipelineConfiguration.cs
similarity index 63%
rename from src/Ocelot/Middleware/OcelotMiddlewareConfiguration.cs
rename to src/Ocelot/Middleware/OcelotPipelineConfiguration.cs
index e3d22928..3cfded9b 100644
--- a/src/Ocelot/Middleware/OcelotMiddlewareConfiguration.cs
+++ b/src/Ocelot/Middleware/OcelotPipelineConfiguration.cs
@@ -1,44 +1,43 @@
-namespace Ocelot.Middleware
-{
- using System;
- using System.Threading.Tasks;
- using Microsoft.AspNetCore.Http;
-
- public class OcelotMiddlewareConfiguration
- {
- ///
- /// 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
- /// in the Ocelot pipeline before we go to the global error handler.
- ///
- public Func, Task> PreErrorResponderMiddleware { get; set; }
-
- ///
- /// This is to allow the user to run any extra authentication before the Ocelot authentication
- /// kicks in
- ///
- public Func, Task> PreAuthenticationMiddleware { get; set; }
-
- ///
- /// This allows the user to completely override the ocelot authentication middleware
- ///
- public Func, Task> AuthenticationMiddleware { get; set; }
-
- ///
- /// This is to allow the user to run any extra authorisation before the Ocelot authentication
- /// kicks in
- ///
- public Func, Task> PreAuthorisationMiddleware { get; set; }
-
- ///
- /// This allows the user to completely override the ocelot authorisation middleware
- ///
- public Func, Task> AuthorisationMiddleware { get; set; }
-
- ///
- /// This allows the user to implement there own query string manipulation logic
- ///
- public Func, Task> PreQueryStringBuilderMiddleware { get; set; }
-
- }
-}
\ No newline at end of file
+namespace Ocelot.Middleware
+{
+ using System;
+ using System.Threading.Tasks;
+
+ public class OcelotPipelineConfiguration
+ {
+ ///
+ /// 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
+ /// in the Ocelot pipeline before we go to the global error handler.
+ ///
+ public Func, Task> PreErrorResponderMiddleware { get; set; }
+
+ ///
+ /// This is to allow the user to run any extra authentication before the Ocelot authentication
+ /// kicks in
+ ///
+ public Func, Task> PreAuthenticationMiddleware { get; set; }
+
+ ///
+ /// This allows the user to completely override the ocelot authentication middleware
+ ///
+ public Func, Task> AuthenticationMiddleware { get; set; }
+
+ ///
+ /// This is to allow the user to run any extra authorisation before the Ocelot authentication
+ /// kicks in
+ ///
+ public Func, Task> PreAuthorisationMiddleware { get; set; }
+
+ ///
+ /// This allows the user to completely override the ocelot authorisation middleware
+ ///
+ public Func, Task> AuthorisationMiddleware { get; set; }
+
+ ///
+ /// This allows the user to implement there own query string manipulation logic
+ ///
+ public Func, Task> PreQueryStringBuilderMiddleware { get; set; }
+
+ }
+}
diff --git a/src/Ocelot/Middleware/OcelotRequestDelegate.cs b/src/Ocelot/Middleware/OcelotRequestDelegate.cs
new file mode 100644
index 00000000..130dfd86
--- /dev/null
+++ b/src/Ocelot/Middleware/OcelotRequestDelegate.cs
@@ -0,0 +1,6 @@
+using System.Threading.Tasks;
+
+namespace Ocelot.Middleware
+{
+ public delegate Task OcelotRequestDelegate(DownstreamContext downstreamContext);
+}
diff --git a/src/Ocelot/Middleware/Pipeline/IOcelotPipelineBuilder.cs b/src/Ocelot/Middleware/Pipeline/IOcelotPipelineBuilder.cs
new file mode 100644
index 00000000..3bc0d6b0
--- /dev/null
+++ b/src/Ocelot/Middleware/Pipeline/IOcelotPipelineBuilder.cs
@@ -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 middleware);
+ OcelotRequestDelegate Build();
+ }
+}
diff --git a/src/Ocelot/Middleware/Pipeline/LICENSE.txt b/src/Ocelot/Middleware/Pipeline/LICENSE.txt
new file mode 100644
index 00000000..7b2956ec
--- /dev/null
+++ b/src/Ocelot/Middleware/Pipeline/LICENSE.txt
@@ -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.
diff --git a/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilder.cs b/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilder.cs
new file mode 100644
index 00000000..1e37514c
--- /dev/null
+++ b/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilder.cs
@@ -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> _middlewares;
+
+ public OcelotPipelineBuilder(IServiceProvider provider)
+ {
+ ApplicationServices = provider;
+ _middlewares = new List>();
+ }
+
+ public IServiceProvider ApplicationServices { get; }
+
+ public OcelotPipelineBuilder Use(Func 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;
+ }
+ }
+}
diff --git a/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilderExtensions.cs b/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilderExtensions.cs
new file mode 100644
index 00000000..422097fc
--- /dev/null
+++ b/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilderExtensions.cs
@@ -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(this IOcelotPipelineBuilder app, params object[] args)
+ {
+ return app.UseMiddleware(typeof(TMiddleware), args);
+ }
+
+ public static IOcelotPipelineBuilder Use(this IOcelotPipelineBuilder app, Func, Task> middleware)
+ {
+ return app.Use(next =>
+ {
+ return context =>
+ {
+ Func 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