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