mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 06:22:50 +08:00
commit
2d01e96369
2
.gitignore
vendored
2
.gitignore
vendored
@ -243,7 +243,7 @@ tools/
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
# Ocelot acceptance test config
|
# Ocelot acceptance test config
|
||||||
test/Ocelot.AcceptanceTests/configuration.json
|
test/Ocelot.AcceptanceTests/ocelot.json
|
||||||
|
|
||||||
# Read the docstates
|
# Read the docstates
|
||||||
_build/
|
_build/
|
||||||
|
31
.travis.yml
Normal file
31
.travis.yml
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
language: csharp
|
||||||
|
os:
|
||||||
|
- osx
|
||||||
|
- linux
|
||||||
|
|
||||||
|
# Ubuntu 14.04
|
||||||
|
sudo: required
|
||||||
|
dist: trusty
|
||||||
|
|
||||||
|
# OS X 10.12
|
||||||
|
osx_image: xcode9.2
|
||||||
|
|
||||||
|
mono:
|
||||||
|
- 4.4.2
|
||||||
|
|
||||||
|
dotnet: 2.1.4
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- git fetch --unshallow # Travis always does a shallow clone, but GitVersion needs the full history including branches and tags
|
||||||
|
- git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
|
||||||
|
- git fetch origin
|
||||||
|
|
||||||
|
script:
|
||||||
|
- ./build.sh
|
||||||
|
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- .packages
|
||||||
|
- tools/Addins
|
||||||
|
- tools/gitreleasemanager
|
||||||
|
- tools/GitVersion.CommandLine
|
@ -1,10 +1,11 @@
|
|||||||
[<img src="http://threemammals.com/images/ocelot_logo.png">](http://threemammals.com/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) Windows (AppVeyor)
|
||||||
|
[](https://travis-ci.org/ThreeMammals/Ocelot) Linux & OSX (Travis)
|
||||||
|
|
||||||
[](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/ThreeMammals/Ocelot?branch=develop)
|
||||||
|
|
||||||
# Ocelot
|
# Ocelot
|
||||||
|
|
||||||
@ -38,8 +39,9 @@ A quick list of Ocelot's capabilities for more information see the [documentatio
|
|||||||
|
|
||||||
* Routing
|
* Routing
|
||||||
* Request Aggregation
|
* Request Aggregation
|
||||||
* Service Discovery with Consul
|
* Service Discovery with Consul & Eureka
|
||||||
* Service Fabric
|
* Service Fabric
|
||||||
|
* WebSockets
|
||||||
* Authentication
|
* Authentication
|
||||||
* Authorisation
|
* Authorisation
|
||||||
* Rate Limiting
|
* Rate Limiting
|
||||||
@ -50,6 +52,7 @@ A quick list of Ocelot's capabilities for more information see the [documentatio
|
|||||||
* Headers / Query String / Claims Transformation
|
* Headers / Query String / Claims Transformation
|
||||||
* Custom Middleware / Delegating Handlers
|
* Custom Middleware / Delegating Handlers
|
||||||
* Configuration / Administration REST API
|
* Configuration / Administration REST API
|
||||||
|
* Platform / Cloud agnostic
|
||||||
|
|
||||||
## How to install
|
## How to install
|
||||||
|
|
||||||
|
38
build.cake
38
build.cake
@ -17,7 +17,7 @@ var artifactsDir = Directory("artifacts");
|
|||||||
// unit testing
|
// unit testing
|
||||||
var artifactsForUnitTestsDir = artifactsDir + Directory("UnitTests");
|
var artifactsForUnitTestsDir = artifactsDir + Directory("UnitTests");
|
||||||
var unitTestAssemblies = @"./test/Ocelot.UnitTests/Ocelot.UnitTests.csproj";
|
var unitTestAssemblies = @"./test/Ocelot.UnitTests/Ocelot.UnitTests.csproj";
|
||||||
var minCodeCoverage = 76.4d;
|
var minCodeCoverage = 82d;
|
||||||
var coverallsRepoToken = "coveralls-repo-token-ocelot";
|
var coverallsRepoToken = "coveralls-repo-token-ocelot";
|
||||||
var coverallsRepo = "https://coveralls.io/github/TomPallister/Ocelot";
|
var coverallsRepo = "https://coveralls.io/github/TomPallister/Ocelot";
|
||||||
|
|
||||||
@ -189,6 +189,24 @@ Task("RunAcceptanceTests")
|
|||||||
.IsDependentOn("Compile")
|
.IsDependentOn("Compile")
|
||||||
.Does(() =>
|
.Does(() =>
|
||||||
{
|
{
|
||||||
|
if(TravisCI.IsRunningOnTravisCI)
|
||||||
|
{
|
||||||
|
Information(
|
||||||
|
@"Job:
|
||||||
|
JobId: {0}
|
||||||
|
JobNumber: {1}
|
||||||
|
OSName: {2}",
|
||||||
|
BuildSystem.TravisCI.Environment.Job.JobId,
|
||||||
|
BuildSystem.TravisCI.Environment.Job.JobNumber,
|
||||||
|
BuildSystem.TravisCI.Environment.Job.OSName
|
||||||
|
);
|
||||||
|
|
||||||
|
if(TravisCI.Environment.Job.OSName.ToLower() == "osx")
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var settings = new DotNetCoreTestSettings
|
var settings = new DotNetCoreTestSettings
|
||||||
{
|
{
|
||||||
Configuration = compileConfig,
|
Configuration = compileConfig,
|
||||||
@ -205,6 +223,24 @@ Task("RunIntegrationTests")
|
|||||||
.IsDependentOn("Compile")
|
.IsDependentOn("Compile")
|
||||||
.Does(() =>
|
.Does(() =>
|
||||||
{
|
{
|
||||||
|
if(TravisCI.IsRunningOnTravisCI)
|
||||||
|
{
|
||||||
|
Information(
|
||||||
|
@"Job:
|
||||||
|
JobId: {0}
|
||||||
|
JobNumber: {1}
|
||||||
|
OSName: {2}",
|
||||||
|
BuildSystem.TravisCI.Environment.Job.JobId,
|
||||||
|
BuildSystem.TravisCI.Environment.Job.JobNumber,
|
||||||
|
BuildSystem.TravisCI.Environment.Job.OSName
|
||||||
|
);
|
||||||
|
|
||||||
|
if(TravisCI.Environment.Job.OSName.ToLower() == "osx")
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var settings = new DotNetCoreTestSettings
|
var settings = new DotNetCoreTestSettings
|
||||||
{
|
{
|
||||||
Configuration = compileConfig,
|
Configuration = compileConfig,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
Configuration
|
Configuration
|
||||||
============
|
============
|
||||||
|
|
||||||
An example configuration can be found `here <https://github.com/TomPallister/Ocelot/blob/develop/test/Ocelot.ManualTest/configuration.json>`_.
|
An example configuration can be found `here <https://github.com/TomPallister/Ocelot/blob/develop/test/Ocelot.ManualTest/ocelot.json>`_.
|
||||||
There are two sections to the configuration. An array of ReRoutes and a GlobalConfiguration.
|
There are two sections to the configuration. An array of ReRoutes and a GlobalConfiguration.
|
||||||
The ReRoutes are the objects that tell Ocelot how to treat an upstream request. The Global
|
The ReRoutes are the objects that tell Ocelot how to treat an upstream request. The Global
|
||||||
configuration is a bit hacky and allows overrides of ReRoute specific settings. It's useful
|
configuration is a bit hacky and allows overrides of ReRoute specific settings. It's useful
|
||||||
@ -64,20 +64,12 @@ Here is an example ReRoute configuration, You don't need to set all of these thi
|
|||||||
"UseCookieContainer": true,
|
"UseCookieContainer": true,
|
||||||
"UseTracing": true
|
"UseTracing": true
|
||||||
},
|
},
|
||||||
"UseServiceDiscovery": false
|
"UseServiceDiscovery": false,
|
||||||
|
"DangerousAcceptAnyServerCertificateValidator": false
|
||||||
}
|
}
|
||||||
|
|
||||||
More information on how to use these options is below..
|
More information on how to use these options is below..
|
||||||
|
|
||||||
Follow Redirects / Use CookieContainer
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Use HttpHandlerOptions in ReRoute configuration to set up HttpHandler behavior:
|
|
||||||
- _AllowAutoRedirect_ is a value that indicates whether the request should follow redirection responses.
|
|
||||||
Set it true if the request should automatically follow redirection responses from the Downstream resource; otherwise false. The default value is true.
|
|
||||||
- _UseCookieContainer_ is a value that indicates whether the handler uses the CookieContainer property to store server cookies and uses these cookies when sending requests.
|
|
||||||
The default value is true.
|
|
||||||
|
|
||||||
Multiple environments
|
Multiple environments
|
||||||
^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
@ -92,15 +84,40 @@ to you
|
|||||||
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
|
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
|
||||||
.AddJsonFile("appsettings.json", true, true)
|
.AddJsonFile("appsettings.json", true, true)
|
||||||
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
||||||
.AddJsonFile("configuration.json")
|
.AddJsonFile("ocelot.json")
|
||||||
.AddJsonFile($"configuration.{hostingContext.HostingEnvironment.EnvironmentName}.json")
|
.AddJsonFile($"configuration.{hostingContext.HostingEnvironment.EnvironmentName}.json")
|
||||||
.AddEnvironmentVariables();
|
.AddEnvironmentVariables();
|
||||||
})
|
})
|
||||||
|
|
||||||
Ocelot should now use the environment specific configuration and fall back to configuration.json if there isnt one.
|
Ocelot will now use the environment specific configuration and fall back to ocelot.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>`_.
|
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>`_.
|
||||||
|
|
||||||
|
Merging configuration files
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
This feature was requested in `Issue 296 <https://github.com/ThreeMammals/Ocelot/issues/296>`_ and allows users to have multiple configuration files to make managing large configurations easier.
|
||||||
|
|
||||||
|
Instead of adding the configuration directly e.g. AddJsonFile("ocelot.json") you can call AddOcelot() like below.
|
||||||
|
|
||||||
|
.. code-block:: csharp
|
||||||
|
|
||||||
|
.ConfigureAppConfiguration((hostingContext, config) =>
|
||||||
|
{
|
||||||
|
config
|
||||||
|
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
|
||||||
|
.AddJsonFile("appsettings.json", true, true)
|
||||||
|
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
||||||
|
.AddOcelot()
|
||||||
|
.AddEnvironmentVariables();
|
||||||
|
})
|
||||||
|
|
||||||
|
In this scenario Ocelot will look for any files that match the pattern (?i)ocelot.([a-zA-Z0-9]*).json and then merge these together. If you want to set the GlobalConfiguration property you must have a file called ocelot.global.json.
|
||||||
|
|
||||||
|
The way Ocelot merges the files is basically load them, loop over them, add any ReRoutes, add any AggregateReRoutes and if the file is called ocelot.global.json add the GlobalConfiguration aswell as any ReRoutes or AggregateReRoutes. Ocelot will then save the merged configuration to a file called ocelot.json and this will be used as the source of truth while ocelot is running.
|
||||||
|
|
||||||
|
At the moment there is no validation at this stage it only happens when Ocelot validates the final merged configuration. This is something to be aware of when you are investigating problems. I would advise always checking what is in ocelot.json if you have any problems.
|
||||||
|
|
||||||
Store configuration in consul
|
Store configuration in consul
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
@ -112,7 +129,7 @@ If you add the following when you register your services Ocelot will attempt to
|
|||||||
.AddOcelot()
|
.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 ocelot.json. This is how Ocelot
|
||||||
finds your Consul agent and interacts to load and store the configuration from Consul.
|
finds your Consul agent and interacts to load and store the configuration from Consul.
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
@ -128,3 +145,30 @@ I decided to create this feature after working on the raft consensus algorithm a
|
|||||||
I guess it means if you want to use Ocelot to its fullest you take on Consul as a dependency for now.
|
I guess it means if you want to use Ocelot to its fullest you take on Consul as a dependency for now.
|
||||||
|
|
||||||
This feature has a 3 second ttl cache before making a new request to your local consul agent.
|
This feature has a 3 second ttl cache before making a new request to your local consul agent.
|
||||||
|
|
||||||
|
Follow Redirects / Use CookieContainer
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Use HttpHandlerOptions in ReRoute configuration to set up HttpHandler behavior:
|
||||||
|
|
||||||
|
1. AllowAutoRedirect is a value that indicates whether the request should follow redirection responses. Set it true if the request should automatically
|
||||||
|
follow redirection responses from the Downstream resource; otherwise false. The default value is false.
|
||||||
|
2. 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 false. Please note
|
||||||
|
that if you are using the CookieContainer Ocelot caches the HttpClient for each downstream service. This means that all requests
|
||||||
|
to that DownstreamService will share the same cookies. `Issue 274 <https://github.com/ThreeMammals/Ocelot/issues/274>`_ was created because a user
|
||||||
|
noticed that the cookies were being shared. I tried to think of a nice way to handle this but I think it is impossible. If you don't cache the clients
|
||||||
|
that means each request gets a new client and therefore a new cookie container. If you clear the cookies from the cached client container you get race conditions due to inflight
|
||||||
|
requests. This would also mean that subsequent requests dont use the cookies from the previous response! All in all not a great situation. I would avoid setting
|
||||||
|
UseCookieContainer to true unless you have a really really good reason. Just look at your response headers and forward the cookies back with your next request!
|
||||||
|
|
||||||
|
SSL Errors
|
||||||
|
^^^^^^^^^^
|
||||||
|
|
||||||
|
Id you want to ignore SSL warnings / errors set the following in your ReRoute config.
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
"DangerousAcceptAnyServerCertificateValidator": false
|
||||||
|
|
||||||
|
I don't reccomend doing this, I suggest creating your own certificate and then getting it trusted by your local / remote machine if you can.
|
@ -40,7 +40,7 @@ Or transient as below...
|
|||||||
.AddTransientDelegatingHandler<FakeHandlerTwo>()
|
.AddTransientDelegatingHandler<FakeHandlerTwo>()
|
||||||
|
|
||||||
Both of these Add methods have a default parameter called global which is set to false. If it is false then the intent of
|
Both of these Add methods have a default parameter called global which is set to false. If it is false then the intent of
|
||||||
the DelegatingHandler is to be applied to specific ReRoutes via configuration.json (more on that later). If it is set to true
|
the DelegatingHandler is to be applied to specific ReRoutes via ocelot.json (more on that later). If it is set to true
|
||||||
then it becomes a global handler and will be applied to all ReRoutes.
|
then it becomes a global handler and will be applied to all ReRoutes.
|
||||||
|
|
||||||
e.g.
|
e.g.
|
||||||
@ -58,7 +58,7 @@ Or transient as below...
|
|||||||
.AddTransientDelegatingHandler<FakeHandler>(true)
|
.AddTransientDelegatingHandler<FakeHandler>(true)
|
||||||
|
|
||||||
Finally if you want ReRoute specific DelegatingHandlers or to order your specific and / or global (more on this later) DelegatingHandlers
|
Finally if you want ReRoute specific DelegatingHandlers or to order your specific and / or global (more on this later) DelegatingHandlers
|
||||||
then you must add the following json to the specific ReRoute in configuration.json. The names in the array must match the class names of your
|
then you must add the following json to the specific ReRoute in ocelot.json. The names in the array must match the class names of your
|
||||||
DelegatingHandlers for Ocelot to match them together.
|
DelegatingHandlers for Ocelot to match them together.
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
@ -70,8 +70,8 @@ DelegatingHandlers for Ocelot to match them together.
|
|||||||
|
|
||||||
You can have as many DelegatingHandlers as you want and they are run in the following order:
|
You can have as many DelegatingHandlers as you want and they are run in the following order:
|
||||||
|
|
||||||
1. Any globals that are left in the order they were added to services and are not in the DelegatingHandlers array from configuration.json.
|
1. Any globals that are left in the order they were added to services and are not in the DelegatingHandlers array from ocelot.json.
|
||||||
2. Any non global DelegatingHandlers plus any globals that were in the DelegatingHandlers array from configuration.json ordered as they are in the DelegatingHandlers array.
|
2. Any non global DelegatingHandlers plus any globals that were in the DelegatingHandlers array from ocelot.json ordered as they are in the DelegatingHandlers array.
|
||||||
3. Tracing DelegatingHandler if enabled (see tracing docs).
|
3. Tracing DelegatingHandler if enabled (see tracing docs).
|
||||||
4. QoS DelegatingHandler if enabled (see QoS docs).
|
4. QoS DelegatingHandler if enabled (see QoS docs).
|
||||||
5. The HttpClient sends the HttpRequestMessage.
|
5. The HttpClient sends the HttpRequestMessage.
|
||||||
|
15
docs/features/graphql.rst
Normal file
15
docs/features/graphql.rst
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
GraphQL
|
||||||
|
=======
|
||||||
|
|
||||||
|
OK you got me Ocelot doesn't directly support GraphQL but so many people have asked about it I wanted to show how easy it is to integrate
|
||||||
|
the `graphql-dotnet <https://github.com/graphql-dotnet/graphql-dotnet>`_ library.
|
||||||
|
|
||||||
|
|
||||||
|
Please see the sample project `OcelotGraphQL <https://github.com/ThreeMammals/Ocelot/tree/develop/samples/OcelotGraphQL>`_.
|
||||||
|
Using a combination of the graphql-dotnet project and Ocelot's DelegatingHandler features this is pretty easy to do.
|
||||||
|
However I do not intend to integrate more closely with GraphQL at the moment. Check out the samples readme and that should give
|
||||||
|
you enough instruction on how to do this!
|
||||||
|
|
||||||
|
Good luck and have fun :>
|
||||||
|
|
||||||
|
|
@ -1,10 +1,50 @@
|
|||||||
Headers Transformation
|
Headers Transformation
|
||||||
=====================
|
======================
|
||||||
|
|
||||||
Ocelot allows the user to transform headers pre and post downstream request. At the moment Ocelot only supports find and replace. This feature was requested `GitHub #190 <https://github.com/TomPallister/Ocelot/issues/190>`_ and I decided that it was going to be useful in various ways.
|
Ocelot allows the user to transform headers pre and post downstream request. At the moment Ocelot only supports find and replace. This feature was requested `GitHub #190 <https://github.com/TomPallister/Ocelot/issues/190>`_ and I decided that it was going to be useful in various ways.
|
||||||
|
|
||||||
Syntax
|
Add to Request
|
||||||
^^^^^^
|
^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
This feature was requestes in `GitHub #313 <https://github.com/ThreeMammals/Ocelot/issues/313>`_.
|
||||||
|
|
||||||
|
If you want to add a header to your upstream request please add the following to a ReRoute in your ocelot.json:
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
"UpstreamHeaderTransform": {
|
||||||
|
"Uncle": "Bob"
|
||||||
|
}
|
||||||
|
|
||||||
|
In the example above a header with the key Uncle and value Bob would be send to to the upstream service.
|
||||||
|
|
||||||
|
Placeholders are supported too (see below).
|
||||||
|
|
||||||
|
Add to Response
|
||||||
|
^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
This feature was requested in `GitHub #280 <https://github.com/TomPallister/Ocelot/issues/280>`_.
|
||||||
|
|
||||||
|
If you want to add a header to your downstream response please add the following to a ReRoute in ocelot.json..
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
"DownstreamHeaderTransform": {
|
||||||
|
"Uncle": "Bob"
|
||||||
|
},
|
||||||
|
|
||||||
|
In the example above a header with the key Uncle and value Bob would be returned by Ocelot when requesting the specific ReRoute.
|
||||||
|
|
||||||
|
If you want to return the Butterfly APM trace id then do something like the following..
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
"DownstreamHeaderTransform": {
|
||||||
|
"AnyKey": "{TraceId}"
|
||||||
|
},
|
||||||
|
|
||||||
|
Find and Replace
|
||||||
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
In order to transform a header first we specify the header key and then the type of transform we want e.g.
|
In order to transform a header first we specify the header key and then the type of transform we want e.g.
|
||||||
|
|
||||||
@ -17,7 +57,7 @@ The key is "Test" and the value is "http://www.bbc.co.uk/, http://ocelot.com/".
|
|||||||
Pre Downstream Request
|
Pre Downstream Request
|
||||||
^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Add the following to a ReRoute in configuration.json in order to replace http://www.bbc.co.uk/ with http://ocelot.com/. This header will be changed before the request downstream and will be sent to the downstream server.
|
Add the following to a ReRoute in ocelot.json in order to replace http://www.bbc.co.uk/ with http://ocelot.com/. This header will be changed before the request downstream and will be sent to the downstream server.
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
|
|
||||||
@ -26,9 +66,9 @@ Add the following to a ReRoute in configuration.json in order to replace http://
|
|||||||
},
|
},
|
||||||
|
|
||||||
Post Downstream Request
|
Post Downstream Request
|
||||||
^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Add the following to a ReRoute in configuration.json in order to replace http://www.bbc.co.uk/ with http://ocelot.com/. This transformation will take place after Ocelot has received the response from the downstream service.
|
Add the following to a ReRoute in ocelot.json in order to replace http://www.bbc.co.uk/ with http://ocelot.com/. This transformation will take place after Ocelot has received the response from the downstream service.
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
|
|
||||||
@ -43,6 +83,7 @@ Ocelot allows placeholders that can be used in header transformation.
|
|||||||
|
|
||||||
{BaseUrl} - This will use Ocelot's base url e.g. http://localhost:5000 as its value.
|
{BaseUrl} - This will use Ocelot's base url e.g. http://localhost:5000 as its value.
|
||||||
{DownstreamBaseUrl} - This will use the downstream services base url e.g. http://localhost:5000 as its value. This only works for DownstreamHeaderTransform at the moment.
|
{DownstreamBaseUrl} - This will use the downstream services base url e.g. http://localhost:5000 as its value. This only works for DownstreamHeaderTransform at the moment.
|
||||||
|
{TraceId} - This will use the Butterfly APM Trace Id. This only works for DownstreamHeaderTransform at the moment.
|
||||||
|
|
||||||
Handling 302 Redirects
|
Handling 302 Redirects
|
||||||
^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
@ -16,7 +16,7 @@ You must choose in your configuration which load balancer to use.
|
|||||||
Configuration
|
Configuration
|
||||||
^^^^^^^^^^^^^
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
The following shows how to set up multiple downstream services for a ReRoute using configuration.json and then select the LeadConnection load balancer. This is the simplest way to get load balancing set up.
|
The following shows how to set up multiple downstream services for a ReRoute using ocelot.json and then select the LeadConnection load balancer. This is the simplest way to get load balancing set up.
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
|
|
||||||
|
@ -12,3 +12,9 @@ Finally if logging is set to trace level Ocelot will log starting, finishing and
|
|||||||
The reason for not just using bog standard framework logging is that I could not
|
The reason for not just using bog standard framework logging is that I could not
|
||||||
work out how to override the request id that get's logged when setting IncludeScopes
|
work out how to override the request id that get's logged when setting IncludeScopes
|
||||||
to true for logging settings. Nicely onto the next feature.
|
to true for logging settings. Nicely onto the next feature.
|
||||||
|
|
||||||
|
Warning
|
||||||
|
^^^^^^^
|
||||||
|
|
||||||
|
If you are logging to Console you will get terrible performance. I have had so many issues about performance issues with Ocelot
|
||||||
|
and it is always logging level Debug, logging to Console :) Make sure you are logging to something proper in production :)
|
||||||
|
@ -9,7 +9,7 @@ and override middleware. This is done as follos.
|
|||||||
|
|
||||||
.. code-block:: csharp
|
.. code-block:: csharp
|
||||||
|
|
||||||
var configuration = new OcelotMiddlewareConfiguration
|
var configuration = new OcelotPipelineConfiguration
|
||||||
{
|
{
|
||||||
PreErrorResponderMiddleware = async (ctx, next) =>
|
PreErrorResponderMiddleware = async (ctx, next) =>
|
||||||
{
|
{
|
||||||
|
@ -17,6 +17,17 @@ Add the following section to a ReRoute configuration.
|
|||||||
|
|
||||||
You must set a number greater than 0 against ExceptionsAllowedBeforeBreaking for this rule to be
|
You must set a number greater than 0 against ExceptionsAllowedBeforeBreaking for this rule to be
|
||||||
implemented. Duration of break is how long the circuit breaker will stay open for after it is tripped.
|
implemented. Duration of break is how long the circuit breaker will stay open for after it is tripped.
|
||||||
TimeoutValue means ff a request takes more than 5 seconds it will automatically be timed out.
|
TimeoutValue means if a request takes more than 5 seconds it will automatically be timed out.
|
||||||
|
|
||||||
If you do not add a QoS section QoS will not be used.
|
You can set the TimeoutValue in isoldation of the ExceptionsAllowedBeforeBreaking and DurationOfBreak options.
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
"QoSOptions": {
|
||||||
|
"TimeoutValue":5000
|
||||||
|
}
|
||||||
|
|
||||||
|
There is no point setting the other two in isolation as they affect each other :)
|
||||||
|
|
||||||
|
If you do not add a QoS section QoS will not be used however Ocelot will default to a 90 second timeout
|
||||||
|
on all downstream requests. If someone needs this to be configurable open an issue.
|
@ -23,7 +23,7 @@ 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.
|
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.
|
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
|
You can also set the following in the GlobalConfiguration part of ocelot.json
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
|
|
||||||
|
@ -5,12 +5,114 @@ Ocelot allow's you to specify Aggregate ReRoutes that compose multiple normal Re
|
|||||||
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
|
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.
|
architecture with Ocelot.
|
||||||
|
|
||||||
This feature was requested as part of `Issue 79 <https://github.com/TomPallister/Ocelot/pull/79>`_ .
|
This feature was requested as part of `Issue 79 <https://github.com/TomPallister/Ocelot/pull/79>`_ and further improvements were made as part of `Issue 298 <https://github.com/TomPallister/Ocelot/issue/298>`_.
|
||||||
|
|
||||||
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.
|
In order to set this up you must do something like the following in your ocelot.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.
|
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).
|
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).
|
||||||
|
|
||||||
|
Advanced register your own Aggregators
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Ocelot started with just the basic request aggregation and since then we have added a more advanced method that let's the user take in the responses from the
|
||||||
|
downstream services and then aggregate them into a response object.
|
||||||
|
|
||||||
|
The ocelot.json setup is pretty much the same as the basic aggregation approach apart from you need to add an Aggregator property like 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": "/",
|
||||||
|
"Aggregator": "FakeDefinedAggregator"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Here we have added an aggregator called FakeDefinedAggregator. Ocelot is going to look for this aggregator when it tries to aggregate this ReRoute.
|
||||||
|
|
||||||
|
In order to make the aggregator available we must add the FakeDefinedAggregator to the OcelotBuilder like below.
|
||||||
|
|
||||||
|
.. code-block:: csharp
|
||||||
|
|
||||||
|
services
|
||||||
|
.AddOcelot()
|
||||||
|
.AddSingletonDefinedAggregator<FakeDefinedAggregator>();
|
||||||
|
|
||||||
|
Now when Ocelot tries to aggregate the ReRoute above it will find the FakeDefinedAggregator in the container and use it to aggregate the ReRoute.
|
||||||
|
Because the FakeDefinedAggregator is registered in the container you can add any dependencies it needs into the container like below.
|
||||||
|
|
||||||
|
.. code-block:: csharp
|
||||||
|
|
||||||
|
services.AddSingleton<FooDependency>();
|
||||||
|
|
||||||
|
services
|
||||||
|
.AddOcelot()
|
||||||
|
.AddSingletonDefinedAggregator<FooAggregator>();
|
||||||
|
|
||||||
|
In this example FooAggregator takes a dependency on FooDependency and it will be resolved by the container.
|
||||||
|
|
||||||
|
In addition to this Ocelot lets you add transient aggregators like below.
|
||||||
|
|
||||||
|
.. code-block:: csharp
|
||||||
|
|
||||||
|
services
|
||||||
|
.AddOcelot()
|
||||||
|
.AddTransientDefinedAggregator<FakeDefinedAggregator>();
|
||||||
|
|
||||||
|
In order to make an Aggregator you must implement this interface.
|
||||||
|
|
||||||
|
.. code-block:: csharp
|
||||||
|
|
||||||
|
public interface IDefinedAggregator
|
||||||
|
{
|
||||||
|
Task<DownstreamResponse> Aggregate(List<DownstreamResponse> responses);
|
||||||
|
}
|
||||||
|
|
||||||
|
With this feature you can pretty much do whatever you want because DownstreamResponse contains Content, Headers and Status Code. We can add extra things if needed
|
||||||
|
just raise an issue on GitHub. Please note if the HttpClient throws an exception when making a request to a ReRoute in the aggregate then you will not get a DownstreamResponse for
|
||||||
|
it but you would for any that succeed. If it does throw an exception this will be logged.
|
||||||
|
|
||||||
|
Basic expecting JSON from Downstream Services
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -65,9 +167,6 @@ If the ReRoute /tom returned a body of {"Age": 19} and /laura returned {"Age": 2
|
|||||||
|
|
||||||
{"Tom":{"Age": 19},"Laura":{"Age": 25}}
|
{"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
|
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
|
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.
|
JSON without any pretty spaces etc.
|
||||||
@ -76,20 +175,13 @@ All headers will be lost from the downstream services response.
|
|||||||
|
|
||||||
Ocelot will always return content type application/json with an aggregate request.
|
Ocelot will always return content type application/json with an aggregate request.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Gotcha's / Further info
|
||||||
|
-----------------------
|
||||||
|
|
||||||
You cannot use ReRoutes with specific RequestIdKeys as this would be crazy complicated to track.
|
You cannot use ReRoutes with specific RequestIdKeys as this would be crazy complicated to track.
|
||||||
|
|
||||||
Aggregation only supports the GET HTTP Verb.
|
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 order to use the reques tid feature you have two options.
|
|||||||
|
|
||||||
*Global*
|
*Global*
|
||||||
|
|
||||||
In your configuration.json set the following in the GlobalConfiguration section. This will be used for all requests into Ocelot.
|
In your ocelot.json set the following in the GlobalConfiguration section. This will be used for all requests into Ocelot.
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ I reccomend using the GlobalConfiguration unless you really need it to be ReRout
|
|||||||
|
|
||||||
*ReRoute*
|
*ReRoute*
|
||||||
|
|
||||||
If you want to override this for a specific ReRoute add the following to configuration.json for the specific ReRoute.
|
If you want to override this for a specific ReRoute add the following to ocelot.json for the specific ReRoute.
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
|
|
||||||
|
@ -140,3 +140,41 @@ If you do not set UpstreamHost on a ReRoue then any host header can match it. Th
|
|||||||
preservers existing functionality at the time of building the feature. This means that if you have two ReRoutes that are the same apart from the UpstreamHost where one is null and the other set. Ocelot will favour the one that has been set.
|
preservers existing functionality at the time of building the feature. This means that if you have two ReRoutes that are the same apart from the UpstreamHost where one is null and the other set. Ocelot will favour the one that has been set.
|
||||||
|
|
||||||
This feature was requested as part of `Issue 216 <https://github.com/TomPallister/Ocelot/pull/216>`_ .
|
This feature was requested as part of `Issue 216 <https://github.com/TomPallister/Ocelot/pull/216>`_ .
|
||||||
|
|
||||||
|
Priority
|
||||||
|
^^^^^^^^
|
||||||
|
|
||||||
|
In `Issue 270 <https://github.com/TomPallister/Ocelot/pull/270>`_ I finally decided to expose the ReRoute priority in
|
||||||
|
ocelot.json. This means you can decide in what order you want your ReRoutes to match the Upstream HttpRequest.
|
||||||
|
|
||||||
|
In order to get this working add the following to a ReRoute in ocelot.json, 0 is just an example value here but will explain below.
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"Priority": 0
|
||||||
|
}
|
||||||
|
|
||||||
|
0 is the lowest priority, Ocelot will always use 0 for /{catchAll} ReRoutes and this is still hardcoded. After that you are free
|
||||||
|
to set any priority you wish.
|
||||||
|
|
||||||
|
e.g. you could have
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"UpstreamPathTemplate": "/goods/{catchAll}"
|
||||||
|
"Priority": 0
|
||||||
|
}
|
||||||
|
|
||||||
|
and
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"UpstreamPathTemplate": "/goods/delete"
|
||||||
|
"Priority": 1
|
||||||
|
}
|
||||||
|
|
||||||
|
In the example above if you make a request into Ocelot on /goods/delete Ocelot will match /goods/delete ReRoute. Previously it would have
|
||||||
|
matched /goods/{catchAll} (because this is the first ReRoute in the list!).
|
@ -38,3 +38,53 @@ and LeastConnection algorithm you can use. If no load balancer is specified Ocel
|
|||||||
}
|
}
|
||||||
|
|
||||||
When this is set up Ocelot will lookup the downstream host and port from the service discover provider and load balance requests across any available services.
|
When this is set up Ocelot will lookup the downstream host and port from the service discover provider and load balance requests across any available services.
|
||||||
|
|
||||||
|
ACL Token
|
||||||
|
---------
|
||||||
|
|
||||||
|
If you are using ACL with Consul Ocelot supports adding the X-Consul-Token header. In order so this to work you must add the additional property below.
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
"ServiceDiscoveryProvider": {
|
||||||
|
"Host": "localhost",
|
||||||
|
"Port": 9500,
|
||||||
|
"Token": "footoken"
|
||||||
|
}
|
||||||
|
|
||||||
|
Ocelot will add this token to the consul client that it uses to make requests and that is then used for every request.
|
||||||
|
|
||||||
|
Eureka
|
||||||
|
^^^^^^
|
||||||
|
|
||||||
|
This feature was requested as part of `Issue 262 <https://github.com/TomPallister/Ocelot/issue/262>`_ . to add support for Netflix's
|
||||||
|
Eureka service discovery provider. The main reason for this is it is a key part of `Steeltoe <https://steeltoe.io/>`_ which is something
|
||||||
|
to do with `Pivotal <https://pivotal.io/platform>`_! Anyway enough of the background.
|
||||||
|
|
||||||
|
In order to get this working add the following to ocelot.json..
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
"ServiceDiscoveryProvider": {
|
||||||
|
"Type": "Eureka"
|
||||||
|
}
|
||||||
|
|
||||||
|
And following the guide `Here <https://steeltoe.io/docs/steeltoe-discovery/>`_ you may also need to add some stuff to appsettings.json. For example the json below tells the steeltoe / pivotal services where to look for the service discovery server and if the service should register with it.
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
"eureka": {
|
||||||
|
"client": {
|
||||||
|
"serviceUrl": "http://localhost:8761/eureka/",
|
||||||
|
"shouldRegisterWithEureka": false,
|
||||||
|
"shouldFetchRegistry": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
I am told that if shouldRegisterWithEureka is false then shouldFetchRegistry will defaut to true so you don't need it explicitly but left it in there.
|
||||||
|
|
||||||
|
Ocelot will now register all the necessary services when it starts up and if you have the json above will register itself with
|
||||||
|
Eureka. One of the services polls Eureka every 30 seconds (default) and gets the latest service state and persists this in memory.
|
||||||
|
When Ocelot asks for a given service it is retrieved from memory so performance is not a big problem. Please note that this code
|
||||||
|
is provided by the Pivotal.Discovery.Client NuGet package so big thanks to them for all the hard work.
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ In your ConfigureServices method
|
|||||||
option.Service = "Ocelot";
|
option.Service = "Ocelot";
|
||||||
});
|
});
|
||||||
|
|
||||||
Then in your configuration.json add the following to the ReRoute you want to trace..
|
Then in your ocelot.json add the following to the ReRoute you want to trace..
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
|
|
||||||
|
68
docs/features/websockets.rst
Normal file
68
docs/features/websockets.rst
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
Websockets
|
||||||
|
==========
|
||||||
|
|
||||||
|
Ocelot supports proxying websockets with some extra bits. This functionality was requested in `Issue 212 <https://github.com/ThreeMammals/Ocelot/issues/212>`_.
|
||||||
|
|
||||||
|
In order to get websocket proxying working with Ocelot you need to do the following.
|
||||||
|
|
||||||
|
In your Configure method you need to tell your application to use WebSockets.
|
||||||
|
|
||||||
|
.. code-block:: csharp
|
||||||
|
|
||||||
|
Configure(app =>
|
||||||
|
{
|
||||||
|
app.UseWebSockets();
|
||||||
|
app.UseOcelot().Wait();
|
||||||
|
})
|
||||||
|
|
||||||
|
Then in your ocelot.json add the following to proxy a ReRoute using websockets.
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/ws",
|
||||||
|
"UpstreamPathTemplate": "/",
|
||||||
|
"DownstreamScheme": "ws",
|
||||||
|
"DownstreamHostAndPorts": [
|
||||||
|
{
|
||||||
|
"Host": "localhost",
|
||||||
|
"Port": 5001
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
With this configuration set Ocelot will match any websocket traffic that comes in on / and proxy it to localhost:5001/ws. To make this clearer
|
||||||
|
Ocelot will receive messages from the upstream client, proxy these to the downstream service, receive messages from the downstream service and
|
||||||
|
proxy these to the upstream client.
|
||||||
|
|
||||||
|
Supported
|
||||||
|
^^^^^^^^^
|
||||||
|
|
||||||
|
1. Load Balancer
|
||||||
|
2. Routing
|
||||||
|
3. Service Discovery
|
||||||
|
|
||||||
|
This means that you can set up your downstream services running websockets and either have multiple DownstreamHostAndPorts in your ReRoute
|
||||||
|
config or hook your ReRoute into a service discovery provider and then load balance requests...Which I think is pretty cool :)
|
||||||
|
|
||||||
|
Not Supported
|
||||||
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Unfortunately a lot of Ocelot's features are non websocket specific such as header and http client stuff. I've listed what won't work below.
|
||||||
|
|
||||||
|
1. Tracing
|
||||||
|
2. RequestId
|
||||||
|
3. Request Aggregation
|
||||||
|
4. Rate Limiting
|
||||||
|
5. Quality of Service
|
||||||
|
6. Middleware Injection
|
||||||
|
7. Header Transformation
|
||||||
|
8. Delegating Handlers
|
||||||
|
9. Claims Transformation
|
||||||
|
10. Caching
|
||||||
|
11. Authentication - If anyone requests it we might be able to do something with basic authentication.
|
||||||
|
12. Authorisation
|
||||||
|
|
||||||
|
I'm not 100% sure what will happen with this feature when it get's into the wild so please make sure you test thoroughly!
|
||||||
|
|
||||||
|
|
@ -21,10 +21,12 @@ Thanks for taking a look at the Ocelot documentation. Please use the left hand n
|
|||||||
features/configuration
|
features/configuration
|
||||||
features/routing
|
features/routing
|
||||||
features/requestaggregation
|
features/requestaggregation
|
||||||
|
features/graphql
|
||||||
features/servicediscovery
|
features/servicediscovery
|
||||||
features/servicefabric
|
features/servicefabric
|
||||||
features/authentication
|
features/authentication
|
||||||
features/authorisation
|
features/authorisation
|
||||||
|
features/websockets
|
||||||
features/administration
|
features/administration
|
||||||
features/ratelimiting
|
features/ratelimiting
|
||||||
features/caching
|
features/caching
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
Big Picture
|
Big Picture
|
||||||
===========
|
===========
|
||||||
|
|
||||||
Ocleot is aimed at people using .NET running
|
Ocelot 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.
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ built to netcoreapp2.0 `this <https://docs.microsoft.com/en-us/dotnet/articles/s
|
|||||||
|
|
||||||
**Install NuGet package**
|
**Install NuGet package**
|
||||||
|
|
||||||
Install Ocelot and it's dependecies using nuget. You will need to create a netcoreapp2.0 projct and bring the package into it. Then follow the Startup below and :doc:`../features/configuration` sections
|
Install Ocelot and it's dependencies using nuget. You will need to create a netcoreapp2.0 project and bring the package into it. Then follow the Startup below and :doc:`../features/configuration` sections
|
||||||
to get up and running.
|
to get up and running.
|
||||||
|
|
||||||
``Install-Package Ocelot``
|
``Install-Package Ocelot``
|
||||||
@ -18,7 +18,7 @@ All versions can be found `here <https://www.nuget.org/packages/Ocelot/>`_.
|
|||||||
|
|
||||||
**Configuration**
|
**Configuration**
|
||||||
|
|
||||||
The following is a very basic configuration.json. It won't do anything but should get Ocelot starting.
|
The following is a very basic ocelot.json. It won't do anything but should get Ocelot starting.
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ AddOcelot() (adds ocelot services), UseOcelot().Wait() (sets up all the Ocelot m
|
|||||||
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
|
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
|
||||||
.AddJsonFile("appsettings.json", true, true)
|
.AddJsonFile("appsettings.json", true, true)
|
||||||
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
||||||
.AddJsonFile("configuration.json")
|
.AddJsonFile("ocelot.json")
|
||||||
.AddEnvironmentVariables();
|
.AddEnvironmentVariables();
|
||||||
})
|
})
|
||||||
.ConfigureServices(s => {
|
.ConfigureServices(s => {
|
||||||
@ -87,7 +87,7 @@ All versions can be found `here <https://www.nuget.org/packages/Ocelot/>`_.
|
|||||||
|
|
||||||
**Configuration**
|
**Configuration**
|
||||||
|
|
||||||
The following is a very basic configuration.json. It won't do anything but should get Ocelot starting.
|
The following is a very basic ocelot.json. It won't do anything but should get Ocelot starting.
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
|
|
||||||
@ -135,7 +135,7 @@ An example startup using a json file for configuration can be seen below.
|
|||||||
.SetBasePath(env.ContentRootPath)
|
.SetBasePath(env.ContentRootPath)
|
||||||
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
|
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
|
||||||
.AddJsonFile("configuration.json")
|
.AddJsonFile("ocelot.json")
|
||||||
.AddEnvironmentVariables();
|
.AddEnvironmentVariables();
|
||||||
|
|
||||||
Configuration = builder.Build();
|
Configuration = builder.Build();
|
||||||
|
@ -7,7 +7,10 @@ Ocelot does not support...
|
|||||||
|
|
||||||
* Fowarding a host header - The host header that you send to Ocelot will not be forwarded to the downstream service. Obviously this would break everything :(
|
* Fowarding a host header - The host header that you send to Ocelot will not be forwarded to the downstream service. Obviously this would break everything :(
|
||||||
|
|
||||||
* Swagger - I have looked multiple times at building swagger.json out of the Ocelot configuration.json but it doesnt fit into the vision I have for Ocelot. If you would like to have Swagger in Ocelot then you must roll your own swagger.json and do the following in your Startup.cs or Program.cs. The code sample below registers a piece of middleware that loads your hand rolled swagger.json and returns it on /swagger/v1/swagger.json. It then registers the SwaggerUI middleware from Swashbuckle.AspNetCore
|
* Swagger - I have looked multiple times at building swagger.json out of the Ocelot ocelot.json but it doesnt fit into the vision
|
||||||
|
I have for Ocelot. If you would like to have Swagger in Ocelot then you must roll your own swagger.json and do the following in your
|
||||||
|
Startup.cs or Program.cs. The code sample below registers a piece of middleware that loads your hand rolled swagger.json and returns
|
||||||
|
it on /swagger/v1/swagger.json. It then registers the SwaggerUI middleware from Swashbuckle.AspNetCore
|
||||||
|
|
||||||
.. code-block:: csharp
|
.. code-block:: csharp
|
||||||
|
|
||||||
@ -25,8 +28,16 @@ Ocelot does not support...
|
|||||||
|
|
||||||
app.UseOcelot().Wait();
|
app.UseOcelot().Wait();
|
||||||
|
|
||||||
The main reasons why I don't think Swagger makes sense is we already hand roll our definition in configuration.json. If we want people developing against Ocelot to be able to see what routes are available then either share the configuration.json with them (This should be as easy as granting access to a repo etc) or use the Ocelot administration API so that they can query Ocelot for the configuration.
|
The main reasons why I don't think Swagger makes sense is we already hand roll our definition in ocelot.json.
|
||||||
|
If we want people developing against Ocelot to be able to see what routes are available then either share the ocelot.json
|
||||||
|
with them (This should be as easy as granting access to a repo etc) or use the Ocelot administration API so that they can query Ocelot for the configuration.
|
||||||
|
|
||||||
In addition to this many people will configure Ocelot to proxy all traffic like /products/{everything} to there product service and you would not be describing what is actually available if you parsed this and turned it into a Swagger path. Also Ocelot has no concept of the models that the downstream services can return and linking to the above problem the same endpoint can return multiple models. Ocelot does not know what models might be used in POST, PUT etc so it all gets a bit messy and finally the Swashbuckle package doesnt reload swagger.json if it changes during runtime. Ocelot's configuration can change during runtime so the Swagger and Ocelot information would not match. Unless I rolled my own Swagger implementation.
|
In addition to this many people will configure Ocelot to proxy all traffic like /products/{everything} to there product service
|
||||||
|
and you would not be describing what is actually available if you parsed this and turned it into a Swagger path. Also Ocelot has
|
||||||
|
no concept of the models that the downstream services can return and linking to the above problem the same endpoint can return
|
||||||
|
multiple models. Ocelot does not know what models might be used in POST, PUT etc so it all gets a bit messy and finally the Swashbuckle
|
||||||
|
package doesnt reload swagger.json if it changes during runtime. Ocelot's configuration can change during runtime so the Swagger and Ocelot
|
||||||
|
information would not match. Unless I rolled my own Swagger implementation.
|
||||||
|
|
||||||
If the user wants something to easily test against the Ocelot API then I suggest using Postman as a simple way to do this. It might even be possible to write something that maps configuration.json to the postman json spec. However I don't intend to do this.
|
If the user wants something to easily test against the Ocelot API then I suggest using Postman as a simple way to do this. It might
|
||||||
|
even be possible to write something that maps ocelot.json to the postman json spec. However I don't intend to do this.
|
18
samples/OcelotGraphQL/OcelotGraphQL.csproj
Normal file
18
samples/OcelotGraphQL/OcelotGraphQL.csproj
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="ocelot.json;appsettings.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="wwwroot\"/>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.6"/>
|
||||||
|
<PackageReference Include="Ocelot" Version="5.5.1"/>
|
||||||
|
<PackageReference Include="GraphQL" Version="2.0.0-alpha-870"/>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
131
samples/OcelotGraphQL/Program.cs
Normal file
131
samples/OcelotGraphQL/Program.cs
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Ocelot.Middleware;
|
||||||
|
using Ocelot.DependencyInjection;
|
||||||
|
using GraphQL.Types;
|
||||||
|
using GraphQL;
|
||||||
|
using Ocelot.Requester;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace OcelotGraphQL
|
||||||
|
{
|
||||||
|
public class Hero
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Query
|
||||||
|
{
|
||||||
|
private List<Hero> _heroes = new List<Hero>
|
||||||
|
{
|
||||||
|
new Hero { Id = 1, Name = "R2-D2" },
|
||||||
|
new Hero { Id = 2, Name = "Batman" },
|
||||||
|
new Hero { Id = 3, Name = "Wonder Woman" },
|
||||||
|
new Hero { Id = 4, Name = "Tom Pallister" }
|
||||||
|
};
|
||||||
|
|
||||||
|
[GraphQLMetadata("hero")]
|
||||||
|
public Hero GetHero(int id)
|
||||||
|
{
|
||||||
|
return _heroes.FirstOrDefault(x => x.Id == id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GraphQlDelegatingHandler : DelegatingHandler
|
||||||
|
{
|
||||||
|
private readonly ISchema _schema;
|
||||||
|
|
||||||
|
public GraphQlDelegatingHandler(ISchema schema)
|
||||||
|
{
|
||||||
|
_schema = schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
//try get query from body, could check http method :)
|
||||||
|
var query = await request.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
//if not body try query string, dont hack like this in real world..
|
||||||
|
if(query.Length == 0)
|
||||||
|
{
|
||||||
|
var decoded = WebUtility.UrlDecode(request.RequestUri.Query);
|
||||||
|
query = decoded.Replace("?query=", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = _schema.Execute(_ =>
|
||||||
|
{
|
||||||
|
_.Query = query;
|
||||||
|
});
|
||||||
|
|
||||||
|
//maybe check for errors and headers etc in real world?
|
||||||
|
var response = new HttpResponseMessage(HttpStatusCode.OK)
|
||||||
|
{
|
||||||
|
Content = new StringContent(result)
|
||||||
|
};
|
||||||
|
|
||||||
|
//ocelot will treat this like any other http request...
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
var schema = Schema.For(@"
|
||||||
|
type Hero {
|
||||||
|
id: Int
|
||||||
|
name: String
|
||||||
|
}
|
||||||
|
|
||||||
|
type Query {
|
||||||
|
hero(id: Int): Hero
|
||||||
|
}
|
||||||
|
", _ => {
|
||||||
|
_.Types.Include<Query>();
|
||||||
|
});
|
||||||
|
|
||||||
|
new WebHostBuilder()
|
||||||
|
.UseKestrel()
|
||||||
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
|
.ConfigureAppConfiguration((hostingContext, config) =>
|
||||||
|
{
|
||||||
|
config
|
||||||
|
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
|
||||||
|
.AddJsonFile("appsettings.json", true, true)
|
||||||
|
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
||||||
|
.AddJsonFile("ocelot.json")
|
||||||
|
.AddEnvironmentVariables();
|
||||||
|
})
|
||||||
|
.ConfigureServices(s => {
|
||||||
|
s.AddSingleton<ISchema>(schema);
|
||||||
|
s.AddOcelot()
|
||||||
|
.AddSingletonDelegatingHandler<GraphQlDelegatingHandler>();
|
||||||
|
})
|
||||||
|
.ConfigureLogging((hostingContext, logging) =>
|
||||||
|
{
|
||||||
|
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
|
||||||
|
logging.AddConsole();
|
||||||
|
})
|
||||||
|
.UseIISIntegration()
|
||||||
|
.Configure(app =>
|
||||||
|
{
|
||||||
|
app.UseOcelot().Wait();
|
||||||
|
})
|
||||||
|
.Build()
|
||||||
|
.Run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
71
samples/OcelotGraphQL/README.md
Normal file
71
samples/OcelotGraphQL/README.md
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
# Ocelot using GraphQL example
|
||||||
|
|
||||||
|
Loads of people keep asking me if Ocelot will every support GraphQL, in my mind Ocelot and GraphQL are two different things that can work together.
|
||||||
|
I would not try and implement GraphQL in Ocelot instead I would either have Ocelot in front of GraphQL to handle things like authorisation / authentication or I would
|
||||||
|
bring in the awesome [graphql-dotnet](https://github.com/graphql-dotnet/graphql-dotnet) library and use it in a [DelegatingHandler](http://ocelot.readthedocs.io/en/latest/features/delegatinghandlers.html). This way you could have Ocelot and GraphQL without the extra hop to GraphQL. This same is an example of how to do that.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
If you run this project with
|
||||||
|
|
||||||
|
$ dotnet run
|
||||||
|
|
||||||
|
Use postman or something to make the following requests and you can see Ocelot and GraphQL in action together...
|
||||||
|
|
||||||
|
GET http://localhost:5000/graphql?query={ hero(id: 4) { id name } }
|
||||||
|
|
||||||
|
RESPONSE
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"hero": {
|
||||||
|
"id": 4,
|
||||||
|
"name": "Tom Pallister"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
POST http://localhost:5000/graphql
|
||||||
|
|
||||||
|
BODY
|
||||||
|
```json
|
||||||
|
{ hero(id: 4) { id name } }
|
||||||
|
```
|
||||||
|
|
||||||
|
RESPONSE
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"hero": {
|
||||||
|
"id": 4,
|
||||||
|
"name": "Tom Pallister"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
Please note this project never goes out to another service, it just gets the data for GraphQL in memory. You would need to add the details of your GraphQL server in ocelot.json e.g.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"ReRoutes": [
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/graphql",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHostAndPorts": [
|
||||||
|
{
|
||||||
|
"Host": "yourgraphqlhost.com",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"UpstreamPathTemplate": "/graphql",
|
||||||
|
"DelegatingHandlers": [
|
||||||
|
"GraphQlDelegatingHandler"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
19
samples/OcelotGraphQL/ocelot.json
Normal file
19
samples/OcelotGraphQL/ocelot.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"ReRoutes": [
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHostAndPorts": [
|
||||||
|
{
|
||||||
|
"Host": "jsonplaceholder.typicode.com",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"UpstreamPathTemplate": "/graphql",
|
||||||
|
"DelegatingHandlers": [
|
||||||
|
"GraphQlDelegatingHandler"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
2
samples/OcelotServiceFabric/.gitignore
vendored
2
samples/OcelotServiceFabric/.gitignore
vendored
@ -13,7 +13,7 @@
|
|||||||
# Service fabric
|
# Service fabric
|
||||||
OcelotApplicationApiGatewayPkg/Code
|
OcelotApplicationApiGatewayPkg/Code
|
||||||
OcelotApplication/OcelotApplicationApiGatewayPkg/Code/appsettings.json
|
OcelotApplication/OcelotApplicationApiGatewayPkg/Code/appsettings.json
|
||||||
OcelotApplication/OcelotApplicationApiGatewayPkg/Code/configuration.json
|
OcelotApplication/OcelotApplicationApiGatewayPkg/Code/ocelot.json
|
||||||
OcelotApplication/OcelotApplicationApiGatewayPkg/Code/runtimes/
|
OcelotApplication/OcelotApplicationApiGatewayPkg/Code/runtimes/
|
||||||
OcelotApplicationServicePkg/Code
|
OcelotApplicationServicePkg/Code
|
||||||
OcelotApplication/OcelotApplicationApiGatewayPkg/Code/web.config
|
OcelotApplication/OcelotApplicationApiGatewayPkg/Code/web.config
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<PackageId>OcelotApplicationApiGateway</PackageId>
|
<PackageId>OcelotApplicationApiGateway</PackageId>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Update="configuration.json;appsettings.json;">
|
<None Update="ocelot.json;appsettings.json;">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -67,7 +67,7 @@ namespace OcelotApplicationApiGateway
|
|||||||
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
|
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
|
||||||
.AddJsonFile("appsettings.json", true, true)
|
.AddJsonFile("appsettings.json", true, true)
|
||||||
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
||||||
.AddJsonFile("configuration.json")
|
.AddJsonFile("ocelot.json")
|
||||||
.AddEnvironmentVariables();
|
.AddEnvironmentVariables();
|
||||||
})
|
})
|
||||||
.ConfigureLogging((hostingContext, logging) =>
|
.ConfigureLogging((hostingContext, logging) =>
|
||||||
|
@ -12,20 +12,19 @@ namespace Ocelot.Authentication.Middleware
|
|||||||
public class AuthenticationMiddleware : OcelotMiddleware
|
public class AuthenticationMiddleware : OcelotMiddleware
|
||||||
{
|
{
|
||||||
private readonly OcelotRequestDelegate _next;
|
private readonly OcelotRequestDelegate _next;
|
||||||
private readonly IOcelotLogger _logger;
|
|
||||||
|
|
||||||
public AuthenticationMiddleware(OcelotRequestDelegate next,
|
public AuthenticationMiddleware(OcelotRequestDelegate next,
|
||||||
IOcelotLoggerFactory loggerFactory)
|
IOcelotLoggerFactory loggerFactory)
|
||||||
|
: base(loggerFactory.CreateLogger<AuthenticationMiddleware>())
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_logger = loggerFactory.CreateLogger<AuthenticationMiddleware>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Invoke(DownstreamContext context)
|
public async Task Invoke(DownstreamContext context)
|
||||||
{
|
{
|
||||||
if (IsAuthenticatedRoute(context.DownstreamReRoute))
|
if (IsAuthenticatedRoute(context.DownstreamReRoute))
|
||||||
{
|
{
|
||||||
_logger.LogDebug($"{context.HttpContext.Request.Path} is an authenticated route. {MiddlewareName} checking if client is authenticated");
|
Logger.LogInformation($"{context.HttpContext.Request.Path} is an authenticated route. {MiddlewareName} checking if client is authenticated");
|
||||||
|
|
||||||
var result = await context.HttpContext.AuthenticateAsync(context.DownstreamReRoute.AuthenticationOptions.AuthenticationProviderKey);
|
var result = await context.HttpContext.AuthenticateAsync(context.DownstreamReRoute.AuthenticationOptions.AuthenticationProviderKey);
|
||||||
|
|
||||||
@ -33,25 +32,22 @@ namespace Ocelot.Authentication.Middleware
|
|||||||
|
|
||||||
if (context.HttpContext.User.Identity.IsAuthenticated)
|
if (context.HttpContext.User.Identity.IsAuthenticated)
|
||||||
{
|
{
|
||||||
_logger.LogDebug($"Client has been authenticated for {context.HttpContext.Request.Path}");
|
Logger.LogInformation($"Client has been authenticated for {context.HttpContext.Request.Path}");
|
||||||
await _next.Invoke(context);
|
await _next.Invoke(context);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var error = new List<Error>
|
var error = new UnauthenticatedError(
|
||||||
{
|
$"Request for authenticated route {context.HttpContext.Request.Path} by {context.HttpContext.User.Identity.Name} was unauthenticated");
|
||||||
new UnauthenticatedError(
|
|
||||||
$"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.HttpContext.Request.Path} and pipeline error set. {error.ToErrorString()}");
|
Logger.LogWarning($"Client has NOT been authenticated for {context.HttpContext.Request.Path} and pipeline error set. {error}");
|
||||||
|
|
||||||
SetPipelineError(context, error);
|
SetPipelineError(context, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.LogTrace($"No authentication needed for {context.HttpContext.Request.Path}");
|
Logger.LogInformation($"No authentication needed for {context.HttpContext.Request.Path}");
|
||||||
|
|
||||||
await _next.Invoke(context);
|
await _next.Invoke(context);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using Ocelot.Errors;
|
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
|
|
||||||
namespace Ocelot.Authorisation
|
namespace Ocelot.Authorisation
|
||||||
@ -32,19 +31,13 @@ namespace Ocelot.Authorisation
|
|||||||
var authorised = values.Data.Contains(required.Value);
|
var authorised = values.Data.Contains(required.Value);
|
||||||
if (!authorised)
|
if (!authorised)
|
||||||
{
|
{
|
||||||
return new ErrorResponse<bool>(new List<Error>
|
return new ErrorResponse<bool>(new ClaimValueNotAuthorisedError(
|
||||||
{
|
$"claim value: {values.Data} is not the same as required value: {required.Value} for type: {required.Key}"));
|
||||||
new ClaimValueNotAuthorisedError(
|
|
||||||
$"claim value: {values.Data} is not the same as required value: {required.Value} for type: {required.Key}")
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return new ErrorResponse<bool>(new List<Error>
|
return new ErrorResponse<bool>(new UserDoesNotHaveClaimError($"user does not have claim {required.Key}"));
|
||||||
{
|
|
||||||
new UserDoesNotHaveClaimError($"user does not have claim {required.Key}")
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,30 +13,29 @@
|
|||||||
private readonly OcelotRequestDelegate _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;
|
|
||||||
|
|
||||||
public AuthorisationMiddleware(OcelotRequestDelegate next,
|
public AuthorisationMiddleware(OcelotRequestDelegate next,
|
||||||
IClaimsAuthoriser claimsAuthoriser,
|
IClaimsAuthoriser claimsAuthoriser,
|
||||||
IScopesAuthoriser scopesAuthoriser,
|
IScopesAuthoriser scopesAuthoriser,
|
||||||
IOcelotLoggerFactory loggerFactory)
|
IOcelotLoggerFactory loggerFactory)
|
||||||
|
:base(loggerFactory.CreateLogger<AuthorisationMiddleware>())
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_claimsAuthoriser = claimsAuthoriser;
|
_claimsAuthoriser = claimsAuthoriser;
|
||||||
_scopesAuthoriser = scopesAuthoriser;
|
_scopesAuthoriser = scopesAuthoriser;
|
||||||
_logger = loggerFactory.CreateLogger<AuthorisationMiddleware>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Invoke(DownstreamContext context)
|
public async Task Invoke(DownstreamContext context)
|
||||||
{
|
{
|
||||||
if (IsAuthenticatedRoute(context.DownstreamReRoute))
|
if (IsAuthenticatedRoute(context.DownstreamReRoute))
|
||||||
{
|
{
|
||||||
_logger.LogDebug("route is authenticated scopes must be checked");
|
Logger.LogInformation("route is authenticated scopes must be checked");
|
||||||
|
|
||||||
var authorised = _scopesAuthoriser.Authorise(context.HttpContext.User, context.DownstreamReRoute.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.LogWarning("error authorising user scopes");
|
||||||
|
|
||||||
SetPipelineError(context, authorised.Errors);
|
SetPipelineError(context, authorised.Errors);
|
||||||
return;
|
return;
|
||||||
@ -44,29 +43,26 @@
|
|||||||
|
|
||||||
if (IsAuthorised(authorised))
|
if (IsAuthorised(authorised))
|
||||||
{
|
{
|
||||||
_logger.LogDebug("user scopes is authorised calling next authorisation checks");
|
Logger.LogInformation("user scopes is authorised calling next authorisation checks");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.LogDebug("user scopes is not authorised setting pipeline error");
|
Logger.LogWarning("user scopes is not authorised setting pipeline error");
|
||||||
|
|
||||||
SetPipelineError(context, new List<Error>
|
SetPipelineError(context, new UnauthorisedError(
|
||||||
{
|
$"{context.HttpContext.User.Identity.Name} unable to access {context.DownstreamReRoute.UpstreamPathTemplate.Value}"));
|
||||||
new UnauthorisedError(
|
|
||||||
$"{context.HttpContext.User.Identity.Name} unable to access {context.DownstreamReRoute.UpstreamPathTemplate.Value}")
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsAuthorisedRoute(context.DownstreamReRoute))
|
if (IsAuthorisedRoute(context.DownstreamReRoute))
|
||||||
{
|
{
|
||||||
_logger.LogDebug("route is authorised");
|
Logger.LogInformation("route is authorised");
|
||||||
|
|
||||||
var authorised = _claimsAuthoriser.Authorise(context.HttpContext.User, context.DownstreamReRoute.RouteClaimsRequirement);
|
var authorised = _claimsAuthoriser.Authorise(context.HttpContext.User, context.DownstreamReRoute.RouteClaimsRequirement);
|
||||||
|
|
||||||
if (authorised.IsError)
|
if (authorised.IsError)
|
||||||
{
|
{
|
||||||
_logger.LogDebug($"Error whilst authorising {context.HttpContext.User.Identity.Name} for {context.HttpContext.User.Identity.Name}. Setting pipeline error");
|
Logger.LogWarning($"Error whilst authorising {context.HttpContext.User.Identity.Name}. Setting pipeline error");
|
||||||
|
|
||||||
SetPipelineError(context, authorised.Errors);
|
SetPipelineError(context, authorised.Errors);
|
||||||
return;
|
return;
|
||||||
@ -74,22 +70,19 @@
|
|||||||
|
|
||||||
if (IsAuthorised(authorised))
|
if (IsAuthorised(authorised))
|
||||||
{
|
{
|
||||||
_logger.LogDebug($"{context.HttpContext.User.Identity.Name} has succesfully been authorised for {context.DownstreamReRoute.UpstreamPathTemplate.Value}. Calling next middleware");
|
Logger.LogInformation($"{context.HttpContext.User.Identity.Name} has succesfully been authorised for {context.DownstreamReRoute.UpstreamPathTemplate.Value}.");
|
||||||
await _next.Invoke(context);
|
await _next.Invoke(context);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.LogDebug($"{context.HttpContext.User.Identity.Name} is not authorised to access {context.DownstreamReRoute.UpstreamPathTemplate.Value}. Setting pipeline error");
|
Logger.LogWarning($"{context.HttpContext.User.Identity.Name} is not authorised to access {context.DownstreamReRoute.UpstreamPathTemplate.Value}. Setting pipeline error");
|
||||||
|
|
||||||
SetPipelineError(context, new List<Error>
|
SetPipelineError(context, new UnauthorisedError($"{context.HttpContext.User.Identity.Name} is not authorised to access {context.DownstreamReRoute.UpstreamPathTemplate.Value}"));
|
||||||
{
|
|
||||||
new UnauthorisedError($"{context.HttpContext.User.Identity.Name} is not authorised to access {context.DownstreamReRoute.UpstreamPathTemplate.Value}")
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.LogDebug($"{context.DownstreamReRoute.DownstreamPathTemplate.Value} route does not require user to be authorised");
|
Logger.LogInformation($"{context.DownstreamReRoute.DownstreamPathTemplate.Value} route does not require user to be authorised");
|
||||||
await _next.Invoke(context);
|
await _next.Invoke(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using IdentityModel;
|
using IdentityModel;
|
||||||
using Ocelot.Errors;
|
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
@ -38,11 +37,8 @@ namespace Ocelot.Authorisation
|
|||||||
|
|
||||||
if (matchesScopes.Count == 0)
|
if (matchesScopes.Count == 0)
|
||||||
{
|
{
|
||||||
return new ErrorResponse<bool>(new List<Error>
|
return new ErrorResponse<bool>(
|
||||||
{
|
new ScopeNotAuthorisedError($"no one user scope: '{string.Join(",", userScopes)}' match with some allowed scope: '{string.Join(",", routeAllowedScopes)}'"));
|
||||||
new ScopeNotAuthorisedError(
|
|
||||||
$"no one user scope: '{string.Join(",", userScopes)}' match with some allowed scope: '{string.Join(",", routeAllowedScopes)}'")
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new OkResponse<bool>(true);
|
return new OkResponse<bool>(true);
|
||||||
|
@ -2,19 +2,16 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
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;
|
using Ocelot.Middleware.Multiplexer;
|
||||||
|
|
||||||
namespace Ocelot.Cache.Middleware
|
namespace Ocelot.Cache.Middleware
|
||||||
{
|
{
|
||||||
public class OutputCacheMiddleware : OcelotMiddleware
|
public class OutputCacheMiddleware : OcelotMiddleware
|
||||||
{
|
{
|
||||||
private readonly OcelotRequestDelegate _next;
|
private readonly OcelotRequestDelegate _next;
|
||||||
private readonly IOcelotLogger _logger;
|
|
||||||
private readonly IOcelotCache<CachedResponse> _outputCache;
|
private readonly IOcelotCache<CachedResponse> _outputCache;
|
||||||
private readonly IRegionCreator _regionCreator;
|
private readonly IRegionCreator _regionCreator;
|
||||||
|
|
||||||
@ -22,10 +19,10 @@ namespace Ocelot.Cache.Middleware
|
|||||||
IOcelotLoggerFactory loggerFactory,
|
IOcelotLoggerFactory loggerFactory,
|
||||||
IOcelotCache<CachedResponse> outputCache,
|
IOcelotCache<CachedResponse> outputCache,
|
||||||
IRegionCreator regionCreator)
|
IRegionCreator regionCreator)
|
||||||
|
:base(loggerFactory.CreateLogger<OutputCacheMiddleware>())
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_outputCache = outputCache;
|
_outputCache = outputCache;
|
||||||
_logger = loggerFactory.CreateLogger<OutputCacheMiddleware>();
|
|
||||||
_regionCreator = regionCreator;
|
_regionCreator = regionCreator;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,31 +34,31 @@ namespace Ocelot.Cache.Middleware
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var downstreamUrlKey = $"{context.DownstreamRequest.Method.Method}-{context.DownstreamRequest.RequestUri.OriginalString}";
|
var downstreamUrlKey = $"{context.DownstreamRequest.Method}-{context.DownstreamRequest.OriginalString}";
|
||||||
|
|
||||||
_logger.LogDebug("started checking cache for {downstreamUrlKey}", downstreamUrlKey);
|
Logger.LogDebug($"Started checking cache for {downstreamUrlKey}");
|
||||||
|
|
||||||
var cached = _outputCache.Get(downstreamUrlKey, context.DownstreamReRoute.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}");
|
||||||
|
|
||||||
var response = CreateHttpResponseMessage(cached);
|
var response = CreateHttpResponseMessage(cached);
|
||||||
SetHttpResponseMessageThisRequest(context, response);
|
SetHttpResponseMessageThisRequest(context, response);
|
||||||
|
|
||||||
_logger.LogDebug("finished returned cached response for {downstreamUrlKey}", downstreamUrlKey);
|
Logger.LogDebug($"finished returned cached response for {downstreamUrlKey}");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogDebug("no resonse cached for {downstreamUrlKey}", downstreamUrlKey);
|
Logger.LogDebug($"no resonse cached for {downstreamUrlKey}");
|
||||||
|
|
||||||
await _next.Invoke(context);
|
await _next.Invoke(context);
|
||||||
|
|
||||||
if (context.IsError)
|
if (context.IsError)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("there was a pipeline error for {downstreamUrlKey}", downstreamUrlKey);
|
Logger.LogDebug($"there was a pipeline error for {downstreamUrlKey}");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -70,41 +67,34 @@ namespace Ocelot.Cache.Middleware
|
|||||||
|
|
||||||
_outputCache.Add(downstreamUrlKey, cached, TimeSpan.FromSeconds(context.DownstreamReRoute.CacheOptions.TtlSeconds), context.DownstreamReRoute.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}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetHttpResponseMessageThisRequest(DownstreamContext context, HttpResponseMessage response)
|
private void SetHttpResponseMessageThisRequest(DownstreamContext context, DownstreamResponse response)
|
||||||
{
|
{
|
||||||
context.DownstreamResponse = response;
|
context.DownstreamResponse = response;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal HttpResponseMessage CreateHttpResponseMessage(CachedResponse cached)
|
internal DownstreamResponse CreateHttpResponseMessage(CachedResponse cached)
|
||||||
{
|
{
|
||||||
if (cached == null)
|
if (cached == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var response = new HttpResponseMessage(cached.StatusCode);
|
|
||||||
|
|
||||||
foreach (var header in cached.Headers)
|
|
||||||
{
|
|
||||||
response.Headers.Add(header.Key, header.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
var content = new MemoryStream(Convert.FromBase64String(cached.Body));
|
var content = new MemoryStream(Convert.FromBase64String(cached.Body));
|
||||||
|
|
||||||
response.Content = new StreamContent(content);
|
var streamContent = new StreamContent(content);
|
||||||
|
|
||||||
foreach (var header in cached.ContentHeaders)
|
foreach (var header in cached.ContentHeaders)
|
||||||
{
|
{
|
||||||
response.Content.Headers.Add(header.Key, header.Value);
|
streamContent.Headers.Add(header.Key, header.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
return new DownstreamResponse(streamContent, cached.StatusCode, cached.Headers.ToList());
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<CachedResponse> CreateCachedResponse(HttpResponseMessage response)
|
internal async Task<CachedResponse> CreateCachedResponse(DownstreamResponse response)
|
||||||
{
|
{
|
||||||
if (response == null)
|
if (response == null)
|
||||||
{
|
{
|
||||||
@ -112,7 +102,7 @@ namespace Ocelot.Cache.Middleware
|
|||||||
}
|
}
|
||||||
|
|
||||||
var statusCode = response.StatusCode;
|
var statusCode = response.StatusCode;
|
||||||
var headers = response.Headers.ToDictionary(v => v.Key, v => v.Value);
|
var headers = response.Headers.ToDictionary(v => v.Key, v => v.Values);
|
||||||
string body = null;
|
string body = null;
|
||||||
|
|
||||||
if (response.Content != null)
|
if (response.Content != null)
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
using System.Net.Http;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Ocelot.Cache;
|
|
||||||
using Ocelot.Configuration.Provider;
|
|
||||||
|
|
||||||
namespace Ocelot.Cache
|
namespace Ocelot.Cache
|
||||||
{
|
{
|
||||||
@ -11,7 +7,7 @@ namespace Ocelot.Cache
|
|||||||
[Route("outputcache")]
|
[Route("outputcache")]
|
||||||
public class OutputCacheController : Controller
|
public class OutputCacheController : Controller
|
||||||
{
|
{
|
||||||
private IOcelotCache<CachedResponse> _cache;
|
private readonly IOcelotCache<CachedResponse> _cache;
|
||||||
|
|
||||||
public OutputCacheController(IOcelotCache<CachedResponse> cache)
|
public OutputCacheController(IOcelotCache<CachedResponse> cache)
|
||||||
{
|
{
|
||||||
|
@ -12,28 +12,27 @@ namespace Ocelot.Claims.Middleware
|
|||||||
{
|
{
|
||||||
private readonly OcelotRequestDelegate _next;
|
private readonly OcelotRequestDelegate _next;
|
||||||
private readonly IAddClaimsToRequest _addClaimsToRequest;
|
private readonly IAddClaimsToRequest _addClaimsToRequest;
|
||||||
private readonly IOcelotLogger _logger;
|
|
||||||
|
|
||||||
public ClaimsBuilderMiddleware(OcelotRequestDelegate next,
|
public ClaimsBuilderMiddleware(OcelotRequestDelegate next,
|
||||||
IOcelotLoggerFactory loggerFactory,
|
IOcelotLoggerFactory loggerFactory,
|
||||||
IAddClaimsToRequest addClaimsToRequest)
|
IAddClaimsToRequest addClaimsToRequest)
|
||||||
|
:base(loggerFactory.CreateLogger<ClaimsBuilderMiddleware>())
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_addClaimsToRequest = addClaimsToRequest;
|
_addClaimsToRequest = addClaimsToRequest;
|
||||||
_logger = loggerFactory.CreateLogger<ClaimsBuilderMiddleware>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Invoke(DownstreamContext context)
|
public async Task Invoke(DownstreamContext context)
|
||||||
{
|
{
|
||||||
if (context.DownstreamReRoute.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(context.DownstreamReRoute.ClaimsToClaims, context.HttpContext);
|
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(context, result.Errors);
|
SetPipelineError(context, result.Errors);
|
||||||
return;
|
return;
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
|
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Authentication
|
|
||||||
{
|
|
||||||
public class HashMatcher : IHashMatcher
|
|
||||||
{
|
|
||||||
public bool Match(string password, string salt, string hash)
|
|
||||||
{
|
|
||||||
byte[] s = Convert.FromBase64String(salt);
|
|
||||||
|
|
||||||
string hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
|
|
||||||
password: password,
|
|
||||||
salt: s,
|
|
||||||
prf: KeyDerivationPrf.HMACSHA256,
|
|
||||||
iterationCount: 10000,
|
|
||||||
numBytesRequested: 256 / 8));
|
|
||||||
|
|
||||||
return hashed == hash;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
namespace Ocelot.Configuration.Authentication
|
|
||||||
{
|
|
||||||
public interface IHashMatcher
|
|
||||||
{
|
|
||||||
bool Match(string password, string salt, string hash);
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,6 +2,7 @@ using System.Collections.Generic;
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using Ocelot.Values;
|
using Ocelot.Values;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Ocelot.Configuration.Creator;
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Builder
|
namespace Ocelot.Configuration.Builder
|
||||||
{
|
{
|
||||||
@ -37,11 +38,16 @@ namespace Ocelot.Configuration.Builder
|
|||||||
private string _upstreamHost;
|
private string _upstreamHost;
|
||||||
private string _key;
|
private string _key;
|
||||||
private List<string> _delegatingHandlers;
|
private List<string> _delegatingHandlers;
|
||||||
|
private List<AddHeader> _addHeadersToDownstream;
|
||||||
|
private List<AddHeader> _addHeadersToUpstream;
|
||||||
|
private bool _dangerousAcceptAnyServerCertificateValidator;
|
||||||
|
|
||||||
public DownstreamReRouteBuilder()
|
public DownstreamReRouteBuilder()
|
||||||
{
|
{
|
||||||
_downstreamAddresses = new List<DownstreamHostAndPort>();
|
_downstreamAddresses = new List<DownstreamHostAndPort>();
|
||||||
_delegatingHandlers = new List<string>();
|
_delegatingHandlers = new List<string>();
|
||||||
|
_addHeadersToDownstream = new List<AddHeader>();
|
||||||
|
_addHeadersToUpstream = new List<AddHeader>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DownstreamReRouteBuilder WithDownstreamAddresses(List<DownstreamHostAndPort> downstreamAddresses)
|
public DownstreamReRouteBuilder WithDownstreamAddresses(List<DownstreamHostAndPort> downstreamAddresses)
|
||||||
@ -224,6 +230,24 @@ namespace Ocelot.Configuration.Builder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithAddHeadersToDownstream(List<AddHeader> addHeadersToDownstream)
|
||||||
|
{
|
||||||
|
_addHeadersToDownstream = addHeadersToDownstream;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithAddHeadersToUpstream(List<AddHeader> addHeadersToUpstream)
|
||||||
|
{
|
||||||
|
_addHeadersToUpstream = addHeadersToUpstream;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithDangerousAcceptAnyServerCertificateValidator(bool dangerousAcceptAnyServerCertificateValidator)
|
||||||
|
{
|
||||||
|
_dangerousAcceptAnyServerCertificateValidator = dangerousAcceptAnyServerCertificateValidator;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public DownstreamReRoute Build()
|
public DownstreamReRoute Build()
|
||||||
{
|
{
|
||||||
return new DownstreamReRoute(
|
return new DownstreamReRoute(
|
||||||
@ -253,7 +277,10 @@ namespace Ocelot.Configuration.Builder
|
|||||||
_authenticationOptions,
|
_authenticationOptions,
|
||||||
new PathTemplate(_downstreamPathTemplate),
|
new PathTemplate(_downstreamPathTemplate),
|
||||||
_reRouteKey,
|
_reRouteKey,
|
||||||
_delegatingHandlers);
|
_delegatingHandlers,
|
||||||
|
_addHeadersToDownstream,
|
||||||
|
_addHeadersToUpstream,
|
||||||
|
_dangerousAcceptAnyServerCertificateValidator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ namespace Ocelot.Configuration.Builder
|
|||||||
private List<HttpMethod> _upstreamHttpMethod;
|
private List<HttpMethod> _upstreamHttpMethod;
|
||||||
private string _upstreamHost;
|
private string _upstreamHost;
|
||||||
private List<DownstreamReRoute> _downstreamReRoutes;
|
private List<DownstreamReRoute> _downstreamReRoutes;
|
||||||
|
private string _aggregator;
|
||||||
|
|
||||||
public ReRouteBuilder()
|
public ReRouteBuilder()
|
||||||
{
|
{
|
||||||
@ -56,6 +57,12 @@ namespace Ocelot.Configuration.Builder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ReRouteBuilder WithAggregator(string aggregator)
|
||||||
|
{
|
||||||
|
_aggregator = aggregator;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public ReRoute Build()
|
public ReRoute Build()
|
||||||
{
|
{
|
||||||
return new ReRoute(
|
return new ReRoute(
|
||||||
@ -63,7 +70,8 @@ namespace Ocelot.Configuration.Builder
|
|||||||
new PathTemplate(_upstreamTemplate),
|
new PathTemplate(_upstreamTemplate),
|
||||||
_upstreamHttpMethod,
|
_upstreamHttpMethod,
|
||||||
_upstreamTemplatePattern,
|
_upstreamTemplatePattern,
|
||||||
_upstreamHost
|
_upstreamHost,
|
||||||
|
_aggregator
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,28 +5,35 @@ namespace Ocelot.Configuration.Builder
|
|||||||
private string _serviceDiscoveryProviderHost;
|
private string _serviceDiscoveryProviderHost;
|
||||||
private int _serviceDiscoveryProviderPort;
|
private int _serviceDiscoveryProviderPort;
|
||||||
private string _type;
|
private string _type;
|
||||||
|
private string _token;
|
||||||
|
|
||||||
public ServiceProviderConfigurationBuilder WithServiceDiscoveryProviderHost(string serviceDiscoveryProviderHost)
|
public ServiceProviderConfigurationBuilder WithHost(string serviceDiscoveryProviderHost)
|
||||||
{
|
{
|
||||||
_serviceDiscoveryProviderHost = serviceDiscoveryProviderHost;
|
_serviceDiscoveryProviderHost = serviceDiscoveryProviderHost;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ServiceProviderConfigurationBuilder WithServiceDiscoveryProviderPort(int serviceDiscoveryProviderPort)
|
public ServiceProviderConfigurationBuilder WithPort(int serviceDiscoveryProviderPort)
|
||||||
{
|
{
|
||||||
_serviceDiscoveryProviderPort = serviceDiscoveryProviderPort;
|
_serviceDiscoveryProviderPort = serviceDiscoveryProviderPort;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ServiceProviderConfigurationBuilder WithServiceDiscoveryProviderType(string type)
|
public ServiceProviderConfigurationBuilder WithType(string type)
|
||||||
{
|
{
|
||||||
_type = type;
|
_type = type;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ServiceProviderConfigurationBuilder WithToken(string token)
|
||||||
|
{
|
||||||
|
_token = token;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public ServiceProviderConfiguration Build()
|
public ServiceProviderConfiguration Build()
|
||||||
{
|
{
|
||||||
return new ServiceProviderConfiguration(_type, _serviceDiscoveryProviderHost, _serviceDiscoveryProviderPort);
|
return new ServiceProviderConfiguration(_type, _serviceDiscoveryProviderHost, _serviceDiscoveryProviderPort, _token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
14
src/Ocelot/Configuration/Creator/AddHeader.cs
Normal file
14
src/Ocelot/Configuration/Creator/AddHeader.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
namespace Ocelot.Configuration.Creator
|
||||||
|
{
|
||||||
|
public class AddHeader
|
||||||
|
{
|
||||||
|
public AddHeader(string key, string value)
|
||||||
|
{
|
||||||
|
Key = key;
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Key { get; }
|
||||||
|
public string Value { get; }
|
||||||
|
}
|
||||||
|
}
|
@ -26,8 +26,7 @@ namespace Ocelot.Configuration.Creator
|
|||||||
|
|
||||||
if (claimToThing.IsError)
|
if (claimToThing.IsError)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("ClaimsToThingCreator.BuildAddThingsToRequest",
|
_logger.LogDebug($"Unable to extract configuration for key: {input.Key} and value: {input.Value} your configuration file is incorrect");
|
||||||
$"Unable to extract configuration for key: {input.Key} and value: {input.Value} your configuration file is incorrect");
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -16,9 +16,8 @@ namespace Ocelot.Configuration.Creator
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Register as singleton
|
/// Register as singleton
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class FileOcelotConfigurationCreator : IOcelotConfigurationCreator
|
public class FileInternalConfigurationCreator : IInternalConfigurationCreator
|
||||||
{
|
{
|
||||||
private readonly IOptions<FileConfiguration> _options;
|
|
||||||
private readonly IConfigurationValidator _configurationValidator;
|
private readonly IConfigurationValidator _configurationValidator;
|
||||||
private readonly IOcelotLogger _logger;
|
private readonly IOcelotLogger _logger;
|
||||||
private readonly IClaimsToThingCreator _claimsToThingCreator;
|
private readonly IClaimsToThingCreator _claimsToThingCreator;
|
||||||
@ -35,8 +34,7 @@ namespace Ocelot.Configuration.Creator
|
|||||||
private readonly IHeaderFindAndReplaceCreator _headerFAndRCreator;
|
private readonly IHeaderFindAndReplaceCreator _headerFAndRCreator;
|
||||||
private readonly IDownstreamAddressesCreator _downstreamAddressesCreator;
|
private readonly IDownstreamAddressesCreator _downstreamAddressesCreator;
|
||||||
|
|
||||||
public FileOcelotConfigurationCreator(
|
public FileInternalConfigurationCreator(
|
||||||
IOptions<FileConfiguration> options,
|
|
||||||
IConfigurationValidator configurationValidator,
|
IConfigurationValidator configurationValidator,
|
||||||
IOcelotLoggerFactory loggerFactory,
|
IOcelotLoggerFactory loggerFactory,
|
||||||
IClaimsToThingCreator claimsToThingCreator,
|
IClaimsToThingCreator claimsToThingCreator,
|
||||||
@ -62,9 +60,8 @@ namespace Ocelot.Configuration.Creator
|
|||||||
_requestIdKeyCreator = requestIdKeyCreator;
|
_requestIdKeyCreator = requestIdKeyCreator;
|
||||||
_upstreamTemplatePatternCreator = upstreamTemplatePatternCreator;
|
_upstreamTemplatePatternCreator = upstreamTemplatePatternCreator;
|
||||||
_authOptionsCreator = authOptionsCreator;
|
_authOptionsCreator = authOptionsCreator;
|
||||||
_options = options;
|
|
||||||
_configurationValidator = configurationValidator;
|
_configurationValidator = configurationValidator;
|
||||||
_logger = loggerFactory.CreateLogger<FileOcelotConfigurationCreator>();
|
_logger = loggerFactory.CreateLogger<FileInternalConfigurationCreator>();
|
||||||
_claimsToThingCreator = claimsToThingCreator;
|
_claimsToThingCreator = claimsToThingCreator;
|
||||||
_serviceProviderConfigCreator = serviceProviderConfigCreator;
|
_serviceProviderConfigCreator = serviceProviderConfigCreator;
|
||||||
_qosOptionsCreator = qosOptionsCreator;
|
_qosOptionsCreator = qosOptionsCreator;
|
||||||
@ -72,19 +69,19 @@ namespace Ocelot.Configuration.Creator
|
|||||||
_httpHandlerOptionsCreator = httpHandlerOptionsCreator;
|
_httpHandlerOptionsCreator = httpHandlerOptionsCreator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Response<IOcelotConfiguration>> Create(FileConfiguration fileConfiguration)
|
public async Task<Response<IInternalConfiguration>> Create(FileConfiguration fileConfiguration)
|
||||||
{
|
{
|
||||||
var config = await SetUpConfiguration(fileConfiguration);
|
var config = await SetUpConfiguration(fileConfiguration);
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Response<IOcelotConfiguration>> SetUpConfiguration(FileConfiguration fileConfiguration)
|
private async Task<Response<IInternalConfiguration>> SetUpConfiguration(FileConfiguration fileConfiguration)
|
||||||
{
|
{
|
||||||
var response = await _configurationValidator.IsValid(fileConfiguration);
|
var response = await _configurationValidator.IsValid(fileConfiguration);
|
||||||
|
|
||||||
if (response.Data.IsError)
|
if (response.Data.IsError)
|
||||||
{
|
{
|
||||||
return new ErrorResponse<IOcelotConfiguration>(response.Data.Errors);
|
return new ErrorResponse<IInternalConfiguration>(response.Data.Errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
var reRoutes = new List<ReRoute>();
|
var reRoutes = new List<ReRoute>();
|
||||||
@ -106,9 +103,9 @@ namespace Ocelot.Configuration.Creator
|
|||||||
|
|
||||||
var serviceProviderConfiguration = _serviceProviderConfigCreator.Create(fileConfiguration.GlobalConfiguration);
|
var serviceProviderConfiguration = _serviceProviderConfigCreator.Create(fileConfiguration.GlobalConfiguration);
|
||||||
|
|
||||||
var config = new OcelotConfiguration(reRoutes, _adminPath.Path, serviceProviderConfiguration, fileConfiguration.GlobalConfiguration.RequestIdKey);
|
var config = new InternalConfiguration(reRoutes, _adminPath.Path, serviceProviderConfiguration, fileConfiguration.GlobalConfiguration.RequestIdKey);
|
||||||
|
|
||||||
return new OkResponse<IOcelotConfiguration>(config);
|
return new OkResponse<IInternalConfiguration>(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReRoute SetUpAggregateReRoute(List<ReRoute> reRoutes, FileAggregateReRoute aggregateReRoute, FileGlobalConfiguration globalConfiguration)
|
public ReRoute SetUpAggregateReRoute(List<ReRoute> reRoutes, FileAggregateReRoute aggregateReRoute, FileGlobalConfiguration globalConfiguration)
|
||||||
@ -132,6 +129,7 @@ namespace Ocelot.Configuration.Creator
|
|||||||
.WithUpstreamTemplatePattern(upstreamTemplatePattern)
|
.WithUpstreamTemplatePattern(upstreamTemplatePattern)
|
||||||
.WithDownstreamReRoutes(applicableReRoutes)
|
.WithDownstreamReRoutes(applicableReRoutes)
|
||||||
.WithUpstreamHost(aggregateReRoute.UpstreamHost)
|
.WithUpstreamHost(aggregateReRoute.UpstreamHost)
|
||||||
|
.WithAggregator(aggregateReRoute.Aggregator)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
return reRoute;
|
return reRoute;
|
||||||
@ -213,6 +211,9 @@ namespace Ocelot.Configuration.Creator
|
|||||||
.WithDownstreamHeaderFindAndReplace(hAndRs.Downstream)
|
.WithDownstreamHeaderFindAndReplace(hAndRs.Downstream)
|
||||||
.WithUpstreamHost(fileReRoute.UpstreamHost)
|
.WithUpstreamHost(fileReRoute.UpstreamHost)
|
||||||
.WithDelegatingHandlers(fileReRoute.DelegatingHandlers)
|
.WithDelegatingHandlers(fileReRoute.DelegatingHandlers)
|
||||||
|
.WithAddHeadersToDownstream(hAndRs.AddHeadersToDownstream)
|
||||||
|
.WithAddHeadersToUpstream(hAndRs.AddHeadersToUpstream)
|
||||||
|
.WithDangerousAcceptAnyServerCertificateValidator(fileReRoute.DangerousAcceptAnyServerCertificateValidator)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
return reRoute;
|
return reRoute;
|
@ -1,44 +1,76 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
|
using Ocelot.Infrastructure;
|
||||||
|
using Ocelot.Logging;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Creator
|
namespace Ocelot.Configuration.Creator
|
||||||
{
|
{
|
||||||
public class HeaderFindAndReplaceCreator : IHeaderFindAndReplaceCreator
|
public class HeaderFindAndReplaceCreator : IHeaderFindAndReplaceCreator
|
||||||
{
|
{
|
||||||
private IBaseUrlFinder _finder;
|
private readonly IPlaceholders _placeholders;
|
||||||
private readonly Dictionary<string, Func<string>> _placeholders;
|
private readonly IOcelotLogger _logger;
|
||||||
|
|
||||||
public HeaderFindAndReplaceCreator(IBaseUrlFinder finder)
|
public HeaderFindAndReplaceCreator(IPlaceholders placeholders, IOcelotLoggerFactory factory)
|
||||||
{
|
{
|
||||||
_finder = finder;
|
_logger = factory.CreateLogger<HeaderFindAndReplaceCreator>();
|
||||||
_placeholders = new Dictionary<string, Func<string>>();
|
_placeholders = placeholders;
|
||||||
_placeholders.Add("{BaseUrl}", () => _finder.Find());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public HeaderTransformations Create(FileReRoute fileReRoute)
|
public HeaderTransformations Create(FileReRoute fileReRoute)
|
||||||
{
|
{
|
||||||
var upstream = new List<HeaderFindAndReplace>();
|
var upstream = new List<HeaderFindAndReplace>();
|
||||||
|
var addHeadersToUpstream = new List<AddHeader>();
|
||||||
|
|
||||||
foreach(var input in fileReRoute.UpstreamHeaderTransform)
|
foreach(var input in fileReRoute.UpstreamHeaderTransform)
|
||||||
{
|
{
|
||||||
var hAndr = Map(input);
|
if (input.Value.Contains(","))
|
||||||
upstream.Add(hAndr);
|
{
|
||||||
|
var hAndr = Map(input);
|
||||||
|
if (!hAndr.IsError)
|
||||||
|
{
|
||||||
|
upstream.Add(hAndr.Data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogWarning($"Unable to add UpstreamHeaderTransform {input.Key}: {input.Value}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
addHeadersToUpstream.Add(new AddHeader(input.Key, input.Value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var downstream = new List<HeaderFindAndReplace>();
|
var downstream = new List<HeaderFindAndReplace>();
|
||||||
|
var addHeadersToDownstream = new List<AddHeader>();
|
||||||
|
|
||||||
foreach(var input in fileReRoute.DownstreamHeaderTransform)
|
foreach(var input in fileReRoute.DownstreamHeaderTransform)
|
||||||
{
|
{
|
||||||
var hAndr = Map(input);
|
if(input.Value.Contains(","))
|
||||||
downstream.Add(hAndr);
|
{
|
||||||
|
var hAndr = Map(input);
|
||||||
|
if(!hAndr.IsError)
|
||||||
|
{
|
||||||
|
downstream.Add(hAndr.Data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogWarning($"Unable to add DownstreamHeaderTransform {input.Key}: {input.Value}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
addHeadersToDownstream.Add(new AddHeader(input.Key, input.Value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new HeaderTransformations(upstream, downstream);
|
return new HeaderTransformations(upstream, downstream, addHeadersToDownstream, addHeadersToUpstream);
|
||||||
}
|
}
|
||||||
|
|
||||||
private HeaderFindAndReplace Map(KeyValuePair<string,string> input)
|
private Response<HeaderFindAndReplace> Map(KeyValuePair<string,string> input)
|
||||||
{
|
{
|
||||||
var findAndReplace = input.Value.Split(",");
|
var findAndReplace = input.Value.Split(",");
|
||||||
|
|
||||||
@ -51,16 +83,19 @@ namespace Ocelot.Configuration.Creator
|
|||||||
|
|
||||||
var placeholder = replace.Substring(startOfPlaceholder, startOfPlaceholder + (endOfPlaceholder + 1));
|
var placeholder = replace.Substring(startOfPlaceholder, startOfPlaceholder + (endOfPlaceholder + 1));
|
||||||
|
|
||||||
if(_placeholders.ContainsKey(placeholder))
|
var value = _placeholders.Get(placeholder);
|
||||||
|
|
||||||
|
if(value.IsError)
|
||||||
{
|
{
|
||||||
var value = _placeholders[placeholder].Invoke();
|
return new ErrorResponse<HeaderFindAndReplace>(value.Errors);
|
||||||
replace = replace.Replace(placeholder, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
replace = replace.Replace(placeholder, value.Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
var hAndr = new HeaderFindAndReplace(input.Key, findAndReplace[0], replace, 0);
|
var hAndr = new HeaderFindAndReplace(input.Key, findAndReplace[0], replace, 0);
|
||||||
|
|
||||||
return hAndr;
|
return new OkResponse<HeaderFindAndReplace>(hAndr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,14 +4,23 @@ namespace Ocelot.Configuration.Creator
|
|||||||
{
|
{
|
||||||
public class HeaderTransformations
|
public class HeaderTransformations
|
||||||
{
|
{
|
||||||
public HeaderTransformations(List<HeaderFindAndReplace> upstream, List<HeaderFindAndReplace> downstream)
|
public HeaderTransformations(
|
||||||
|
List<HeaderFindAndReplace> upstream,
|
||||||
|
List<HeaderFindAndReplace> downstream,
|
||||||
|
List<AddHeader> addHeaderToDownstream,
|
||||||
|
List<AddHeader> addHeaderToUpstream)
|
||||||
{
|
{
|
||||||
|
AddHeadersToDownstream = addHeaderToDownstream;
|
||||||
|
AddHeadersToUpstream = addHeaderToUpstream;
|
||||||
Upstream = upstream;
|
Upstream = upstream;
|
||||||
Downstream = downstream;
|
Downstream = downstream;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<HeaderFindAndReplace> Upstream {get;private set;}
|
public List<HeaderFindAndReplace> Upstream { get; }
|
||||||
|
|
||||||
public List<HeaderFindAndReplace> Downstream {get;private set;}
|
public List<HeaderFindAndReplace> Downstream { get; }
|
||||||
|
|
||||||
|
public List<AddHeader> AddHeadersToDownstream { get; }
|
||||||
|
public List<AddHeader> AddHeadersToUpstream { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,24 @@
|
|||||||
using Ocelot.Configuration.File;
|
using Butterfly.Client.Tracing;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
|
using Ocelot.Requester;
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Creator
|
namespace Ocelot.Configuration.Creator
|
||||||
{
|
{
|
||||||
public class HttpHandlerOptionsCreator : IHttpHandlerOptionsCreator
|
public class HttpHandlerOptionsCreator : IHttpHandlerOptionsCreator
|
||||||
{
|
{
|
||||||
|
private IServiceTracer _tracer;
|
||||||
|
|
||||||
|
public HttpHandlerOptionsCreator(IServiceTracer tracer)
|
||||||
|
{
|
||||||
|
_tracer = tracer;
|
||||||
|
}
|
||||||
|
|
||||||
public HttpHandlerOptions Create(FileReRoute fileReRoute)
|
public HttpHandlerOptions Create(FileReRoute fileReRoute)
|
||||||
{
|
{
|
||||||
|
var useTracing = _tracer.GetType() != typeof(FakeServiceTracer) ? fileReRoute.HttpHandlerOptions.UseTracing : false;
|
||||||
|
|
||||||
return new HttpHandlerOptions(fileReRoute.HttpHandlerOptions.AllowAutoRedirect,
|
return new HttpHandlerOptions(fileReRoute.HttpHandlerOptions.AllowAutoRedirect,
|
||||||
fileReRoute.HttpHandlerOptions.UseCookieContainer, fileReRoute.HttpHandlerOptions.UseTracing);
|
fileReRoute.HttpHandlerOptions.UseCookieContainer, useTracing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
|
||||||
|
namespace Ocelot.Configuration.Creator
|
||||||
|
{
|
||||||
|
public interface IInternalConfigurationCreator
|
||||||
|
{
|
||||||
|
Task<Response<IInternalConfiguration>> Create(FileConfiguration fileConfiguration);
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +0,0 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
using Ocelot.Configuration.File;
|
|
||||||
using Ocelot.Responses;
|
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Creator
|
|
||||||
{
|
|
||||||
public interface IOcelotConfigurationCreator
|
|
||||||
{
|
|
||||||
Task<Response<IOcelotConfiguration>> Create(FileConfiguration fileConfiguration);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using IdentityServer4.AccessTokenValidation;
|
|
||||||
using IdentityServer4.Models;
|
|
||||||
using Ocelot.Configuration.Provider;
|
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Creator
|
namespace Ocelot.Configuration.Creator
|
||||||
{
|
{
|
||||||
|
@ -11,9 +11,10 @@ namespace Ocelot.Configuration.Creator
|
|||||||
var serviceProviderPort = globalConfiguration?.ServiceDiscoveryProvider?.Port ?? 0;
|
var serviceProviderPort = globalConfiguration?.ServiceDiscoveryProvider?.Port ?? 0;
|
||||||
|
|
||||||
return new ServiceProviderConfigurationBuilder()
|
return new ServiceProviderConfigurationBuilder()
|
||||||
.WithServiceDiscoveryProviderHost(globalConfiguration?.ServiceDiscoveryProvider?.Host)
|
.WithHost(globalConfiguration?.ServiceDiscoveryProvider?.Host)
|
||||||
.WithServiceDiscoveryProviderPort(serviceProviderPort)
|
.WithPort(serviceProviderPort)
|
||||||
.WithServiceDiscoveryProviderType(globalConfiguration?.ServiceDiscoveryProvider?.Type)
|
.WithType(globalConfiguration?.ServiceDiscoveryProvider?.Type)
|
||||||
|
.WithToken(globalConfiguration?.ServiceDiscoveryProvider?.Token)
|
||||||
.Build();
|
.Build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ namespace Ocelot.Configuration.Creator
|
|||||||
|
|
||||||
if (upstreamTemplate == "/")
|
if (upstreamTemplate == "/")
|
||||||
{
|
{
|
||||||
return new UpstreamPathTemplate(RegExForwardSlashOnly, 1);
|
return new UpstreamPathTemplate(RegExForwardSlashOnly, reRoute.Priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(upstreamTemplate.EndsWith("/"))
|
if(upstreamTemplate.EndsWith("/"))
|
||||||
@ -54,7 +54,7 @@ namespace Ocelot.Configuration.Creator
|
|||||||
? $"^{upstreamTemplate}{RegExMatchEndString}"
|
? $"^{upstreamTemplate}{RegExMatchEndString}"
|
||||||
: $"^{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}";
|
: $"^{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}";
|
||||||
|
|
||||||
return new UpstreamPathTemplate(route, 1);
|
return new UpstreamPathTemplate(route, reRoute.Priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ForwardSlashAndOnePlaceHolder(string upstreamTemplate, List<string> placeholders, int postitionOfPlaceHolderClosingBracket)
|
private bool ForwardSlashAndOnePlaceHolder(string upstreamTemplate, List<string> placeholders, int postitionOfPlaceHolderClosingBracket)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Ocelot.Configuration.Creator;
|
||||||
using Ocelot.Values;
|
using Ocelot.Values;
|
||||||
|
|
||||||
namespace Ocelot.Configuration
|
namespace Ocelot.Configuration
|
||||||
@ -32,8 +33,13 @@ namespace Ocelot.Configuration
|
|||||||
AuthenticationOptions authenticationOptions,
|
AuthenticationOptions authenticationOptions,
|
||||||
PathTemplate downstreamPathTemplate,
|
PathTemplate downstreamPathTemplate,
|
||||||
string reRouteKey,
|
string reRouteKey,
|
||||||
List<string> delegatingHandlers)
|
List<string> delegatingHandlers,
|
||||||
|
List<AddHeader> addHeadersToDownstream,
|
||||||
|
List<AddHeader> addHeadersToUpstream,
|
||||||
|
bool dangerousAcceptAnyServerCertificateValidator)
|
||||||
{
|
{
|
||||||
|
DangerousAcceptAnyServerCertificateValidator = dangerousAcceptAnyServerCertificateValidator;
|
||||||
|
AddHeadersToDownstream = addHeadersToDownstream;
|
||||||
DelegatingHandlers = delegatingHandlers;
|
DelegatingHandlers = delegatingHandlers;
|
||||||
Key = key;
|
Key = key;
|
||||||
UpstreamPathTemplate = upstreamPathTemplate;
|
UpstreamPathTemplate = upstreamPathTemplate;
|
||||||
@ -61,6 +67,7 @@ namespace Ocelot.Configuration
|
|||||||
AuthenticationOptions = authenticationOptions;
|
AuthenticationOptions = authenticationOptions;
|
||||||
DownstreamPathTemplate = downstreamPathTemplate;
|
DownstreamPathTemplate = downstreamPathTemplate;
|
||||||
ReRouteKey = reRouteKey;
|
ReRouteKey = reRouteKey;
|
||||||
|
AddHeadersToUpstream = addHeadersToUpstream;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Key { get; private set; }
|
public string Key { get; private set; }
|
||||||
@ -90,5 +97,8 @@ namespace Ocelot.Configuration
|
|||||||
public PathTemplate DownstreamPathTemplate { get; private set; }
|
public PathTemplate DownstreamPathTemplate { get; private set; }
|
||||||
public string ReRouteKey { get; private set; }
|
public string ReRouteKey { get; private set; }
|
||||||
public List<string> DelegatingHandlers {get;private set;}
|
public List<string> DelegatingHandlers {get;private set;}
|
||||||
|
public List<AddHeader> AddHeadersToDownstream {get;private set;}
|
||||||
|
public List<AddHeader> AddHeadersToUpstream { get; private set; }
|
||||||
|
public bool DangerousAcceptAnyServerCertificateValidator { get; private set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,11 +8,14 @@ namespace Ocelot.Configuration.File
|
|||||||
public string UpstreamPathTemplate { get;set; }
|
public string UpstreamPathTemplate { get;set; }
|
||||||
public string UpstreamHost { get; set; }
|
public string UpstreamHost { get; set; }
|
||||||
public bool ReRouteIsCaseSensitive { get; set; }
|
public bool ReRouteIsCaseSensitive { get; set; }
|
||||||
|
public string Aggregator { get; set; }
|
||||||
|
|
||||||
// Only supports GET..are you crazy!! POST, PUT WOULD BE CRAZY!! :)
|
// Only supports GET..are you crazy!! POST, PUT WOULD BE CRAZY!! :)
|
||||||
public List<string> UpstreamHttpMethod
|
public List<string> UpstreamHttpMethod
|
||||||
{
|
{
|
||||||
get { return new List<string> {"Get"}; }
|
get { return new List<string> {"Get"}; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int Priority {get;set;} = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
{
|
{
|
||||||
public FileHttpHandlerOptions()
|
public FileHttpHandlerOptions()
|
||||||
{
|
{
|
||||||
AllowAutoRedirect = true;
|
AllowAutoRedirect = false;
|
||||||
UseCookieContainer = true;
|
UseCookieContainer = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AllowAutoRedirect { get; set; }
|
public bool AllowAutoRedirect { get; set; }
|
||||||
|
@ -20,6 +20,7 @@ namespace Ocelot.Configuration.File
|
|||||||
UpstreamHeaderTransform = new Dictionary<string, string>();
|
UpstreamHeaderTransform = new Dictionary<string, string>();
|
||||||
DownstreamHostAndPorts = new List<FileHostAndPort>();
|
DownstreamHostAndPorts = new List<FileHostAndPort>();
|
||||||
DelegatingHandlers = new List<string>();
|
DelegatingHandlers = new List<string>();
|
||||||
|
Priority = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string DownstreamPathTemplate { get; set; }
|
public string DownstreamPathTemplate { get; set; }
|
||||||
@ -46,5 +47,8 @@ namespace Ocelot.Configuration.File
|
|||||||
public string UpstreamHost { get; set; }
|
public string UpstreamHost { get; set; }
|
||||||
public string Key { get;set; }
|
public string Key { get;set; }
|
||||||
public List<string> DelegatingHandlers {get;set;}
|
public List<string> DelegatingHandlers {get;set;}
|
||||||
|
public int Priority { get;set; }
|
||||||
|
public int Timeout { get; set; }
|
||||||
|
public bool DangerousAcceptAnyServerCertificateValidator { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,5 +5,6 @@ namespace Ocelot.Configuration.File
|
|||||||
public string Host {get;set;}
|
public string Host {get;set;}
|
||||||
public int Port { get; set; }
|
public int Port { get; set; }
|
||||||
public string Type { get; set; }
|
public string Type { get; set; }
|
||||||
|
public string Token { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,5 +4,6 @@
|
|||||||
{
|
{
|
||||||
string UpstreamPathTemplate { get; set; }
|
string UpstreamPathTemplate { get; set; }
|
||||||
bool ReRouteIsCaseSensitive { get; set; }
|
bool ReRouteIsCaseSensitive { get; set; }
|
||||||
|
int Priority {get;set;}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,34 +2,34 @@ using System;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
using Ocelot.Configuration.Provider;
|
|
||||||
using Ocelot.Configuration.Setter;
|
using Ocelot.Configuration.Setter;
|
||||||
using Ocelot.Raft;
|
using Ocelot.Raft;
|
||||||
using Rafty.Concensus;
|
using Rafty.Concensus;
|
||||||
|
|
||||||
namespace Ocelot.Configuration
|
namespace Ocelot.Configuration
|
||||||
{
|
{
|
||||||
|
using Repository;
|
||||||
|
|
||||||
[Authorize]
|
[Authorize]
|
||||||
[Route("configuration")]
|
[Route("configuration")]
|
||||||
public class FileConfigurationController : Controller
|
public class FileConfigurationController : Controller
|
||||||
{
|
{
|
||||||
private readonly IFileConfigurationProvider _configGetter;
|
private readonly IFileConfigurationRepository _repo;
|
||||||
private readonly IFileConfigurationSetter _configSetter;
|
private readonly IFileConfigurationSetter _setter;
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _provider;
|
||||||
|
|
||||||
public FileConfigurationController(IFileConfigurationProvider getFileConfig, IFileConfigurationSetter configSetter, IServiceProvider serviceProvider)
|
public FileConfigurationController(IFileConfigurationRepository repo, IFileConfigurationSetter setter, IServiceProvider provider)
|
||||||
{
|
{
|
||||||
_configGetter = getFileConfig;
|
_repo = repo;
|
||||||
_configSetter = configSetter;
|
_setter = setter;
|
||||||
_serviceProvider = serviceProvider;
|
_provider = provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<IActionResult> Get()
|
public async Task<IActionResult> Get()
|
||||||
{
|
{
|
||||||
var response = await _configGetter.Get();
|
var response = await _repo.Get();
|
||||||
|
|
||||||
if(response.IsError)
|
if(response.IsError)
|
||||||
{
|
{
|
||||||
@ -43,7 +43,7 @@ namespace Ocelot.Configuration
|
|||||||
public async Task<IActionResult> Post([FromBody]FileConfiguration fileConfiguration)
|
public async Task<IActionResult> Post([FromBody]FileConfiguration fileConfiguration)
|
||||||
{
|
{
|
||||||
//todo - this code is a bit shit sort it out..
|
//todo - this code is a bit shit sort it out..
|
||||||
var test = _serviceProvider.GetService(typeof(INode));
|
var test = _provider.GetService(typeof(INode));
|
||||||
if (test != null)
|
if (test != null)
|
||||||
{
|
{
|
||||||
var node = (INode)test;
|
var node = (INode)test;
|
||||||
@ -56,7 +56,7 @@ namespace Ocelot.Configuration
|
|||||||
return new OkObjectResult(result.Command.Configuration);
|
return new OkObjectResult(result.Command.Configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
var response = await _configSetter.Set(fileConfiguration);
|
var response = await _setter.Set(fileConfiguration);
|
||||||
|
|
||||||
if (response.IsError)
|
if (response.IsError)
|
||||||
{
|
{
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
namespace Ocelot.Configuration
|
||||||
using IdentityServer4.AccessTokenValidation;
|
|
||||||
using IdentityServer4.Models;
|
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Provider
|
|
||||||
{
|
{
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
public interface IIdentityServerConfiguration
|
public interface IIdentityServerConfiguration
|
||||||
{
|
{
|
||||||
string ApiName { get; }
|
string ApiName { get; }
|
@ -2,7 +2,7 @@ using System.Collections.Generic;
|
|||||||
|
|
||||||
namespace Ocelot.Configuration
|
namespace Ocelot.Configuration
|
||||||
{
|
{
|
||||||
public interface IOcelotConfiguration
|
public interface IInternalConfiguration
|
||||||
{
|
{
|
||||||
List<ReRoute> ReRoutes { get; }
|
List<ReRoute> ReRoutes { get; }
|
||||||
string AdministrationPath {get;}
|
string AdministrationPath {get;}
|
@ -1,9 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
namespace Ocelot.Configuration
|
||||||
using IdentityServer4.AccessTokenValidation;
|
|
||||||
using IdentityServer4.Models;
|
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Provider
|
|
||||||
{
|
{
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
public class IdentityServerConfiguration : IIdentityServerConfiguration
|
public class IdentityServerConfiguration : IIdentityServerConfiguration
|
||||||
{
|
{
|
||||||
public IdentityServerConfiguration(
|
public IdentityServerConfiguration(
|
||||||
@ -22,11 +20,11 @@ namespace Ocelot.Configuration.Provider
|
|||||||
CredentialsSigningCertificatePassword = credentialsSigningCertificatePassword;
|
CredentialsSigningCertificatePassword = credentialsSigningCertificatePassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ApiName { get; private set; }
|
public string ApiName { get; }
|
||||||
public bool RequireHttps { get; private set; }
|
public bool RequireHttps { get; }
|
||||||
public List<string> AllowedScopes { get; private set; }
|
public List<string> AllowedScopes { get; }
|
||||||
public string ApiSecret { get; private set; }
|
public string ApiSecret { get; }
|
||||||
public string CredentialsSigningCertificateLocation { get; private set; }
|
public string CredentialsSigningCertificateLocation { get; }
|
||||||
public string CredentialsSigningCertificatePassword { get; private set; }
|
public string CredentialsSigningCertificatePassword { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,9 +2,9 @@ using System.Collections.Generic;
|
|||||||
|
|
||||||
namespace Ocelot.Configuration
|
namespace Ocelot.Configuration
|
||||||
{
|
{
|
||||||
public class OcelotConfiguration : IOcelotConfiguration
|
public class InternalConfiguration : IInternalConfiguration
|
||||||
{
|
{
|
||||||
public OcelotConfiguration(List<ReRoute> reRoutes, string administrationPath, ServiceProviderConfiguration serviceProviderConfiguration, string requestId)
|
public InternalConfiguration(List<ReRoute> reRoutes, string administrationPath, ServiceProviderConfiguration serviceProviderConfiguration, string requestId)
|
||||||
{
|
{
|
||||||
ReRoutes = reRoutes;
|
ReRoutes = reRoutes;
|
||||||
AdministrationPath = administrationPath;
|
AdministrationPath = administrationPath;
|
@ -20,22 +20,14 @@ namespace Ocelot.Configuration.Parser
|
|||||||
|
|
||||||
if (instructions.Length <= 1)
|
if (instructions.Length <= 1)
|
||||||
{
|
{
|
||||||
return new ErrorResponse<ClaimToThing>(
|
return new ErrorResponse<ClaimToThing>(new NoInstructionsError(SplitToken));
|
||||||
new List<Error>
|
|
||||||
{
|
|
||||||
new NoInstructionsError(SplitToken)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var claimMatch = _claimRegex.IsMatch(instructions[0]);
|
var claimMatch = _claimRegex.IsMatch(instructions[0]);
|
||||||
|
|
||||||
if (!claimMatch)
|
if (!claimMatch)
|
||||||
{
|
{
|
||||||
return new ErrorResponse<ClaimToThing>(
|
return new ErrorResponse<ClaimToThing>(new InstructionNotForClaimsError());
|
||||||
new List<Error>
|
|
||||||
{
|
|
||||||
new InstructionNotForClaimsError()
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var newKey = GetIndexValue(instructions[0]);
|
var newKey = GetIndexValue(instructions[0]);
|
||||||
@ -53,11 +45,7 @@ namespace Ocelot.Configuration.Parser
|
|||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
return new ErrorResponse<ClaimToThing>(
|
return new ErrorResponse<ClaimToThing>(new ParsingConfigurationHeaderError(exception));
|
||||||
new List<Error>
|
|
||||||
{
|
|
||||||
new ParsingConfigurationHeaderError(exception)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Ocelot.Configuration.File;
|
|
||||||
using Ocelot.Configuration.Repository;
|
|
||||||
using Ocelot.Responses;
|
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Provider
|
|
||||||
{
|
|
||||||
public class FileConfigurationProvider : IFileConfigurationProvider
|
|
||||||
{
|
|
||||||
private IFileConfigurationRepository _repo;
|
|
||||||
|
|
||||||
public FileConfigurationProvider(IFileConfigurationRepository repo)
|
|
||||||
{
|
|
||||||
_repo = repo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Response<FileConfiguration>> Get()
|
|
||||||
{
|
|
||||||
var fileConfig = await _repo.Get();
|
|
||||||
return new OkResponse<FileConfiguration>(fileConfig.Data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
using Ocelot.Configuration.File;
|
|
||||||
using Ocelot.Responses;
|
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Provider
|
|
||||||
{
|
|
||||||
public interface IFileConfigurationProvider
|
|
||||||
{
|
|
||||||
Task<Response<FileConfiguration>> Get();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
using Ocelot.Configuration.File;
|
|
||||||
using Ocelot.Responses;
|
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Provider
|
|
||||||
{
|
|
||||||
public interface IOcelotConfigurationProvider
|
|
||||||
{
|
|
||||||
Task<Response<IOcelotConfiguration>> Get();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
using Ocelot.Configuration.File;
|
|
||||||
using Ocelot.Configuration.Repository;
|
|
||||||
using Ocelot.Responses;
|
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Provider
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Register as singleton
|
|
||||||
/// </summary>
|
|
||||||
public class OcelotConfigurationProvider : IOcelotConfigurationProvider
|
|
||||||
{
|
|
||||||
private readonly IOcelotConfigurationRepository _config;
|
|
||||||
|
|
||||||
public OcelotConfigurationProvider(IOcelotConfigurationRepository repo)
|
|
||||||
{
|
|
||||||
_config = repo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Response<IOcelotConfiguration>> Get()
|
|
||||||
{
|
|
||||||
var repoConfig = await _config.Get();
|
|
||||||
|
|
||||||
if (repoConfig.IsError)
|
|
||||||
{
|
|
||||||
return new ErrorResponse<IOcelotConfiguration>(repoConfig.Errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new OkResponse<IOcelotConfiguration>(repoConfig.Data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,12 +16,12 @@ namespace Ocelot.Configuration
|
|||||||
TimeoutStrategy = timeoutStrategy;
|
TimeoutStrategy = timeoutStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int ExceptionsAllowedBeforeBreaking { get; private set; }
|
public int ExceptionsAllowedBeforeBreaking { get; }
|
||||||
|
|
||||||
public int DurationOfBreak { get; private set; }
|
public int DurationOfBreak { get; }
|
||||||
|
|
||||||
public int TimeoutValue { get; private set; }
|
public int TimeoutValue { get; }
|
||||||
|
|
||||||
public TimeoutStrategy TimeoutStrategy { get; private set; }
|
public TimeoutStrategy TimeoutStrategy { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,13 +12,15 @@ namespace Ocelot.Configuration
|
|||||||
PathTemplate upstreamPathTemplate,
|
PathTemplate upstreamPathTemplate,
|
||||||
List<HttpMethod> upstreamHttpMethod,
|
List<HttpMethod> upstreamHttpMethod,
|
||||||
UpstreamPathTemplate upstreamTemplatePattern,
|
UpstreamPathTemplate upstreamTemplatePattern,
|
||||||
string upstreamHost)
|
string upstreamHost,
|
||||||
|
string aggregator)
|
||||||
{
|
{
|
||||||
UpstreamHost = upstreamHost;
|
UpstreamHost = upstreamHost;
|
||||||
DownstreamReRoute = downstreamReRoute;
|
DownstreamReRoute = downstreamReRoute;
|
||||||
UpstreamPathTemplate = upstreamPathTemplate;
|
UpstreamPathTemplate = upstreamPathTemplate;
|
||||||
UpstreamHttpMethod = upstreamHttpMethod;
|
UpstreamHttpMethod = upstreamHttpMethod;
|
||||||
UpstreamTemplatePattern = upstreamTemplatePattern;
|
UpstreamTemplatePattern = upstreamTemplatePattern;
|
||||||
|
Aggregator = aggregator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PathTemplate UpstreamPathTemplate { get; private set; }
|
public PathTemplate UpstreamPathTemplate { get; private set; }
|
||||||
@ -26,5 +28,6 @@ namespace Ocelot.Configuration
|
|||||||
public List<HttpMethod> UpstreamHttpMethod { get; private set; }
|
public List<HttpMethod> UpstreamHttpMethod { get; private set; }
|
||||||
public string UpstreamHost { get; private set; }
|
public string UpstreamHost { get; private set; }
|
||||||
public List<DownstreamReRoute> DownstreamReRoute { get; private set; }
|
public List<DownstreamReRoute> DownstreamReRoute { get; private set; }
|
||||||
|
public string Aggregator {get; private set;}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,10 +17,16 @@ namespace Ocelot.Configuration.Repository
|
|||||||
private string _previousAsJson;
|
private string _previousAsJson;
|
||||||
private readonly Timer _timer;
|
private readonly Timer _timer;
|
||||||
private bool _polling;
|
private bool _polling;
|
||||||
|
private readonly IConsulPollerConfiguration _config;
|
||||||
|
|
||||||
public ConsulFileConfigurationPoller(IOcelotLoggerFactory factory, IFileConfigurationRepository repo, IFileConfigurationSetter setter)
|
public ConsulFileConfigurationPoller(
|
||||||
|
IOcelotLoggerFactory factory,
|
||||||
|
IFileConfigurationRepository repo,
|
||||||
|
IFileConfigurationSetter setter,
|
||||||
|
IConsulPollerConfiguration config)
|
||||||
{
|
{
|
||||||
_setter = setter;
|
_setter = setter;
|
||||||
|
_config = config;
|
||||||
_logger = factory.CreateLogger<ConsulFileConfigurationPoller>();
|
_logger = factory.CreateLogger<ConsulFileConfigurationPoller>();
|
||||||
_repo = repo;
|
_repo = repo;
|
||||||
_previousAsJson = "";
|
_previousAsJson = "";
|
||||||
@ -34,18 +40,18 @@ namespace Ocelot.Configuration.Repository
|
|||||||
_polling = true;
|
_polling = true;
|
||||||
await Poll();
|
await Poll();
|
||||||
_polling = false;
|
_polling = false;
|
||||||
}, null, 0, 1000);
|
}, null, 0, _config.Delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Poll()
|
private async Task Poll()
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Started polling consul");
|
_logger.LogInformation("Started polling consul");
|
||||||
|
|
||||||
var fileConfig = await _repo.Get();
|
var fileConfig = await _repo.Get();
|
||||||
|
|
||||||
if(fileConfig.IsError)
|
if(fileConfig.IsError)
|
||||||
{
|
{
|
||||||
_logger.LogDebug($"error geting file config, errors are {string.Join(",", fileConfig.Errors.Select(x => x.Message))}");
|
_logger.LogWarning($"error geting file config, errors are {string.Join(",", fileConfig.Errors.Select(x => x.Message))}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,14 +63,13 @@ namespace Ocelot.Configuration.Repository
|
|||||||
_previousAsJson = asJson;
|
_previousAsJson = asJson;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogDebug("Finished polling consul");
|
_logger.LogInformation("Finished polling consul");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// We could do object comparison here but performance isnt really a problem. This might be an issue one day!
|
/// We could do object comparison here but performance isnt really a problem. This might be an issue one day!
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="config"></param>
|
/// <returns>hash of the config</returns>
|
||||||
/// <returns></returns>
|
|
||||||
private string ToJson(FileConfiguration config)
|
private string ToJson(FileConfiguration config)
|
||||||
{
|
{
|
||||||
var currentHash = JsonConvert.SerializeObject(config);
|
var currentHash = JsonConvert.SerializeObject(config);
|
||||||
|
@ -1,42 +1,60 @@
|
|||||||
using System;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Consul;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Ocelot.Configuration.File;
|
|
||||||
using Ocelot.Responses;
|
|
||||||
using Ocelot.ServiceDiscovery;
|
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Repository
|
namespace Ocelot.Configuration.Repository
|
||||||
{
|
{
|
||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Consul;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
|
using Ocelot.Infrastructure.Consul;
|
||||||
|
using Ocelot.Logging;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
using Ocelot.ServiceDiscovery.Configuration;
|
||||||
|
|
||||||
public class ConsulFileConfigurationRepository : IFileConfigurationRepository
|
public class ConsulFileConfigurationRepository : IFileConfigurationRepository
|
||||||
{
|
{
|
||||||
private readonly ConsulClient _consul;
|
private readonly IConsulClient _consul;
|
||||||
private string _ocelotConfiguration = "OcelotConfiguration";
|
private const string OcelotConfiguration = "InternalConfiguration";
|
||||||
private readonly Cache.IOcelotCache<FileConfiguration> _cache;
|
private readonly Cache.IOcelotCache<FileConfiguration> _cache;
|
||||||
|
private readonly IOcelotLogger _logger;
|
||||||
|
|
||||||
public ConsulFileConfigurationRepository(Cache.IOcelotCache<FileConfiguration> cache, ServiceProviderConfiguration serviceProviderConfig)
|
public ConsulFileConfigurationRepository(
|
||||||
|
Cache.IOcelotCache<FileConfiguration> cache,
|
||||||
|
IInternalConfigurationRepository repo,
|
||||||
|
IConsulClientFactory factory,
|
||||||
|
IOcelotLoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
var consulHost = string.IsNullOrEmpty(serviceProviderConfig?.Host) ? "localhost" : serviceProviderConfig?.Host;
|
_logger = loggerFactory.CreateLogger<ConsulFileConfigurationRepository>();
|
||||||
var consulPort = serviceProviderConfig?.Port ?? 8500;
|
|
||||||
var configuration = new ConsulRegistryConfiguration(consulHost, consulPort, _ocelotConfiguration);
|
|
||||||
_cache = cache;
|
_cache = cache;
|
||||||
_consul = new ConsulClient(c =>
|
|
||||||
|
var internalConfig = repo.Get();
|
||||||
|
|
||||||
|
var consulHost = "localhost";
|
||||||
|
var consulPort = 8500;
|
||||||
|
string token = null;
|
||||||
|
|
||||||
|
if (!internalConfig.IsError)
|
||||||
{
|
{
|
||||||
c.Address = new Uri($"http://{configuration.HostName}:{configuration.Port}");
|
consulHost = string.IsNullOrEmpty(internalConfig.Data.ServiceProviderConfiguration?.Host) ? consulHost : internalConfig.Data.ServiceProviderConfiguration?.Host;
|
||||||
});
|
consulPort = internalConfig.Data.ServiceProviderConfiguration?.Port ?? consulPort;
|
||||||
|
token = internalConfig.Data.ServiceProviderConfiguration?.Token;
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = new ConsulRegistryConfiguration(consulHost, consulPort, OcelotConfiguration, token);
|
||||||
|
|
||||||
|
_consul = factory.Get(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Response<FileConfiguration>> Get()
|
public async Task<Response<FileConfiguration>> Get()
|
||||||
{
|
{
|
||||||
var config = _cache.Get(_ocelotConfiguration, _ocelotConfiguration);
|
var config = _cache.Get(OcelotConfiguration, OcelotConfiguration);
|
||||||
|
|
||||||
if (config != null)
|
if (config != null)
|
||||||
{
|
{
|
||||||
return new OkResponse<FileConfiguration>(config);
|
return new OkResponse<FileConfiguration>(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
var queryResult = await _consul.KV.Get(_ocelotConfiguration);
|
var queryResult = await _consul.KV.Get(OcelotConfiguration);
|
||||||
|
|
||||||
if (queryResult.Response == null)
|
if (queryResult.Response == null)
|
||||||
{
|
{
|
||||||
@ -54,11 +72,11 @@ namespace Ocelot.Configuration.Repository
|
|||||||
|
|
||||||
public async Task<Response> Set(FileConfiguration ocelotConfiguration)
|
public async Task<Response> Set(FileConfiguration ocelotConfiguration)
|
||||||
{
|
{
|
||||||
var json = JsonConvert.SerializeObject(ocelotConfiguration);
|
var json = JsonConvert.SerializeObject(ocelotConfiguration, Formatting.Indented);
|
||||||
|
|
||||||
var bytes = Encoding.UTF8.GetBytes(json);
|
var bytes = Encoding.UTF8.GetBytes(json);
|
||||||
|
|
||||||
var kvPair = new KVPair(_ocelotConfiguration)
|
var kvPair = new KVPair(OcelotConfiguration)
|
||||||
{
|
{
|
||||||
Value = bytes
|
Value = bytes
|
||||||
};
|
};
|
||||||
@ -67,7 +85,7 @@ namespace Ocelot.Configuration.Repository
|
|||||||
|
|
||||||
if (result.Response)
|
if (result.Response)
|
||||||
{
|
{
|
||||||
_cache.AddAndDelete(_ocelotConfiguration, ocelotConfiguration, TimeSpan.FromSeconds(3), _ocelotConfiguration);
|
_cache.AddAndDelete(OcelotConfiguration, ocelotConfiguration, TimeSpan.FromSeconds(3), OcelotConfiguration);
|
||||||
|
|
||||||
return new OkResponse();
|
return new OkResponse();
|
||||||
}
|
}
|
||||||
|
@ -7,15 +7,17 @@ using Ocelot.Responses;
|
|||||||
|
|
||||||
namespace Ocelot.Configuration.Repository
|
namespace Ocelot.Configuration.Repository
|
||||||
{
|
{
|
||||||
public class FileConfigurationRepository : IFileConfigurationRepository
|
public class DiskFileConfigurationRepository : IFileConfigurationRepository
|
||||||
{
|
{
|
||||||
private readonly string _configFilePath;
|
private readonly string _configFilePath;
|
||||||
|
|
||||||
private static readonly object _lock = new object();
|
private static readonly object _lock = new object();
|
||||||
|
|
||||||
public FileConfigurationRepository(IHostingEnvironment hostingEnvironment)
|
private const string ConfigurationFileName = "ocelot";
|
||||||
|
|
||||||
|
public DiskFileConfigurationRepository(IHostingEnvironment hostingEnvironment)
|
||||||
{
|
{
|
||||||
_configFilePath = $"{AppContext.BaseDirectory}/configuration{(string.IsNullOrEmpty(hostingEnvironment.EnvironmentName) ? string.Empty : ".")}{hostingEnvironment.EnvironmentName}.json";
|
_configFilePath = $"{AppContext.BaseDirectory}/{ConfigurationFileName}{(string.IsNullOrEmpty(hostingEnvironment.EnvironmentName) ? string.Empty : ".")}{hostingEnvironment.EnvironmentName}.json";
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<Response<FileConfiguration>> Get()
|
public Task<Response<FileConfiguration>> Get()
|
||||||
@ -34,7 +36,7 @@ namespace Ocelot.Configuration.Repository
|
|||||||
|
|
||||||
public Task<Response> Set(FileConfiguration fileConfiguration)
|
public Task<Response> Set(FileConfiguration fileConfiguration)
|
||||||
{
|
{
|
||||||
string jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
|
string jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration, Formatting.Indented);
|
||||||
|
|
||||||
lock(_lock)
|
lock(_lock)
|
||||||
{
|
{
|
@ -0,0 +1,7 @@
|
|||||||
|
namespace Ocelot.Configuration.Repository
|
||||||
|
{
|
||||||
|
public interface IConsulPollerConfiguration
|
||||||
|
{
|
||||||
|
int Delay { get; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
using Ocelot.Responses;
|
||||||
|
|
||||||
|
namespace Ocelot.Configuration.Repository
|
||||||
|
{
|
||||||
|
public interface IInternalConfigurationRepository
|
||||||
|
{
|
||||||
|
Response<IInternalConfiguration> Get();
|
||||||
|
Response AddOrReplace(IInternalConfiguration internalConfiguration);
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +0,0 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
using Ocelot.Configuration.File;
|
|
||||||
using Ocelot.Responses;
|
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Repository
|
|
||||||
{
|
|
||||||
public interface IOcelotConfigurationRepository
|
|
||||||
{
|
|
||||||
Task<Response<IOcelotConfiguration>> Get();
|
|
||||||
Task<Response> AddOrReplace(IOcelotConfiguration ocelotConfiguration);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,7 @@
|
|||||||
|
namespace Ocelot.Configuration.Repository
|
||||||
|
{
|
||||||
|
public class InMemoryConsulPollerConfiguration : IConsulPollerConfiguration
|
||||||
|
{
|
||||||
|
public int Delay => 1000;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
using Ocelot.Responses;
|
||||||
|
|
||||||
|
namespace Ocelot.Configuration.Repository
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Register as singleton
|
||||||
|
/// </summary>
|
||||||
|
public class InMemoryInternalConfigurationRepository : IInternalConfigurationRepository
|
||||||
|
{
|
||||||
|
private static readonly object LockObject = new object();
|
||||||
|
|
||||||
|
private IInternalConfiguration _internalConfiguration;
|
||||||
|
|
||||||
|
public Response<IInternalConfiguration> Get()
|
||||||
|
{
|
||||||
|
return new OkResponse<IInternalConfiguration>(_internalConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response AddOrReplace(IInternalConfiguration internalConfiguration)
|
||||||
|
{
|
||||||
|
lock (LockObject)
|
||||||
|
{
|
||||||
|
_internalConfiguration = internalConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new OkResponse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,30 +0,0 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
using Ocelot.Responses;
|
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Repository
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Register as singleton
|
|
||||||
/// </summary>
|
|
||||||
public class InMemoryOcelotConfigurationRepository : IOcelotConfigurationRepository
|
|
||||||
{
|
|
||||||
private static readonly object LockObject = new object();
|
|
||||||
|
|
||||||
private IOcelotConfiguration _ocelotConfiguration;
|
|
||||||
|
|
||||||
public Task<Response<IOcelotConfiguration>> Get()
|
|
||||||
{
|
|
||||||
return Task.FromResult<Response<IOcelotConfiguration>>(new OkResponse<IOcelotConfiguration>(_ocelotConfiguration));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<Response> AddOrReplace(IOcelotConfiguration ocelotConfiguration)
|
|
||||||
{
|
|
||||||
lock (LockObject)
|
|
||||||
{
|
|
||||||
_ocelotConfiguration = ocelotConfiguration;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.FromResult<Response>(new OkResponse());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,15 +2,17 @@
|
|||||||
{
|
{
|
||||||
public class ServiceProviderConfiguration
|
public class ServiceProviderConfiguration
|
||||||
{
|
{
|
||||||
public ServiceProviderConfiguration(string type, string host, int port)
|
public ServiceProviderConfiguration(string type, string host, int port, string token)
|
||||||
{
|
{
|
||||||
Host = host;
|
Host = host;
|
||||||
Port = port;
|
Port = port;
|
||||||
|
Token = token;
|
||||||
Type = type;
|
Type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Host { get; private set; }
|
public string Host { get; }
|
||||||
public int Port { get; private set; }
|
public int Port { get; }
|
||||||
public string Type { get; private set; }
|
public string Type { get; }
|
||||||
|
public string Token { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,14 +6,16 @@ using Ocelot.Responses;
|
|||||||
|
|
||||||
namespace Ocelot.Configuration.Setter
|
namespace Ocelot.Configuration.Setter
|
||||||
{
|
{
|
||||||
public class FileConfigurationSetter : IFileConfigurationSetter
|
public class FileAndInternalConfigurationSetter : IFileConfigurationSetter
|
||||||
{
|
{
|
||||||
private readonly IOcelotConfigurationRepository _configRepo;
|
private readonly IInternalConfigurationRepository _configRepo;
|
||||||
private readonly IOcelotConfigurationCreator _configCreator;
|
private readonly IInternalConfigurationCreator _configCreator;
|
||||||
private readonly IFileConfigurationRepository _repo;
|
private readonly IFileConfigurationRepository _repo;
|
||||||
|
|
||||||
public FileConfigurationSetter(IOcelotConfigurationRepository configRepo,
|
public FileAndInternalConfigurationSetter(
|
||||||
IOcelotConfigurationCreator configCreator, IFileConfigurationRepository repo)
|
IInternalConfigurationRepository configRepo,
|
||||||
|
IInternalConfigurationCreator configCreator,
|
||||||
|
IFileConfigurationRepository repo)
|
||||||
{
|
{
|
||||||
_configRepo = configRepo;
|
_configRepo = configRepo;
|
||||||
_configCreator = configCreator;
|
_configCreator = configCreator;
|
||||||
@ -33,7 +35,7 @@ namespace Ocelot.Configuration.Setter
|
|||||||
|
|
||||||
if(!config.IsError)
|
if(!config.IsError)
|
||||||
{
|
{
|
||||||
await _configRepo.AddOrReplace(config.Data);
|
_configRepo.AddOrReplace(config.Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ErrorResponse(config.Errors);
|
return new ErrorResponse(config.Errors);
|
@ -17,8 +17,8 @@ namespace Ocelot.Configuration.Validator
|
|||||||
Errors = errors;
|
Errors = errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsError { get; private set; }
|
public bool IsError { get; }
|
||||||
|
|
||||||
public List<Error> Errors { get; private set; }
|
public List<Error> Errors { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,73 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Microsoft.Extensions.Configuration.Memory;
|
|
||||||
|
|
||||||
namespace Ocelot.DependencyInjection
|
namespace Ocelot.DependencyInjection
|
||||||
{
|
{
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Configuration.Memory;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Configuration.File;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
public static class ConfigurationBuilderExtensions
|
public static class ConfigurationBuilderExtensions
|
||||||
{
|
{
|
||||||
[Obsolete("Please set BaseUrl in configuration.json GlobalConfiguration.BaseUrl")]
|
[Obsolete("Please set BaseUrl in ocelot.json GlobalConfiguration.BaseUrl")]
|
||||||
public static IConfigurationBuilder AddOcelotBaseUrl(this IConfigurationBuilder builder, string baseUrl)
|
public static IConfigurationBuilder AddOcelotBaseUrl(this IConfigurationBuilder builder, string baseUrl)
|
||||||
{
|
{
|
||||||
var memorySource = new MemoryConfigurationSource();
|
var memorySource = new MemoryConfigurationSource
|
||||||
memorySource.InitialData = new List<KeyValuePair<string, string>>
|
|
||||||
{
|
{
|
||||||
new KeyValuePair<string, string>("BaseUrl", baseUrl)
|
InitialData = new List<KeyValuePair<string, string>>
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, string>("BaseUrl", baseUrl)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
builder.Add(memorySource);
|
builder.Add(memorySource);
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IConfigurationBuilder AddOcelot(this IConfigurationBuilder builder)
|
||||||
|
{
|
||||||
|
const string pattern = "(?i)ocelot.([a-zA-Z0-9]*).json";
|
||||||
|
|
||||||
|
var reg = new Regex(pattern);
|
||||||
|
|
||||||
|
var files = Directory.GetFiles(".")
|
||||||
|
.Where(path => reg.IsMatch(path))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var fileConfiguration = new FileConfiguration();
|
||||||
|
|
||||||
|
foreach (var file in files)
|
||||||
|
{
|
||||||
|
// windows and unix sigh...
|
||||||
|
if(files.Count > 1 && (file == "./ocelot.json" || file == ".\\ocelot.json"))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var lines = File.ReadAllText(file);
|
||||||
|
|
||||||
|
var config = JsonConvert.DeserializeObject<FileConfiguration>(lines);
|
||||||
|
|
||||||
|
// windows and unix sigh...
|
||||||
|
if (file == "./ocelot.global.json" || file == ".\\ocelot.global.json")
|
||||||
|
{
|
||||||
|
fileConfiguration.GlobalConfiguration = config.GlobalConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileConfiguration.Aggregates.AddRange(config.Aggregates);
|
||||||
|
fileConfiguration.ReRoutes.AddRange(config.ReRoutes);
|
||||||
|
}
|
||||||
|
|
||||||
|
var json = JsonConvert.SerializeObject(fileConfiguration);
|
||||||
|
|
||||||
|
File.WriteAllText("ocelot.json", json);
|
||||||
|
|
||||||
|
builder.AddJsonFile("ocelot.json");
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ using CacheManager.Core;
|
|||||||
using System;
|
using System;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using IdentityServer4.AccessTokenValidation;
|
using IdentityServer4.AccessTokenValidation;
|
||||||
|
using Ocelot.Middleware.Multiplexer;
|
||||||
|
|
||||||
namespace Ocelot.DependencyInjection
|
namespace Ocelot.DependencyInjection
|
||||||
{
|
{
|
||||||
@ -23,5 +24,10 @@ namespace Ocelot.DependencyInjection
|
|||||||
|
|
||||||
IOcelotBuilder AddTransientDelegatingHandler<T>(bool global = false)
|
IOcelotBuilder AddTransientDelegatingHandler<T>(bool global = false)
|
||||||
where T : DelegatingHandler;
|
where T : DelegatingHandler;
|
||||||
|
|
||||||
|
IOcelotBuilder AddSingletonDefinedAggregator<T>()
|
||||||
|
where T : class, IDefinedAggregator;
|
||||||
|
IOcelotBuilder AddTransientDefinedAggregator<T>()
|
||||||
|
where T : class, IDefinedAggregator;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,3 @@
|
|||||||
using Butterfly.Client.Tracing;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using Ocelot.Middleware.Multiplexer;
|
|
||||||
|
|
||||||
namespace Ocelot.DependencyInjection
|
namespace Ocelot.DependencyInjection
|
||||||
{
|
{
|
||||||
using CacheManager.Core;
|
using CacheManager.Core;
|
||||||
@ -12,17 +8,14 @@ namespace Ocelot.DependencyInjection
|
|||||||
using Ocelot.Authorisation;
|
using Ocelot.Authorisation;
|
||||||
using Ocelot.Cache;
|
using Ocelot.Cache;
|
||||||
using Ocelot.Claims;
|
using Ocelot.Claims;
|
||||||
using Ocelot.Configuration.Authentication;
|
|
||||||
using Ocelot.Configuration.Creator;
|
using Ocelot.Configuration.Creator;
|
||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
using Ocelot.Configuration.Parser;
|
using Ocelot.Configuration.Parser;
|
||||||
using Ocelot.Configuration.Provider;
|
|
||||||
using Ocelot.Configuration.Repository;
|
using Ocelot.Configuration.Repository;
|
||||||
using Ocelot.Configuration.Setter;
|
using Ocelot.Configuration.Setter;
|
||||||
using Ocelot.Configuration.Validator;
|
using Ocelot.Configuration.Validator;
|
||||||
using Ocelot.DownstreamRouteFinder.Finder;
|
using Ocelot.DownstreamRouteFinder.Finder;
|
||||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||||
using Ocelot.DownstreamUrlCreator;
|
|
||||||
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
|
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
|
||||||
using Ocelot.Headers;
|
using Ocelot.Headers;
|
||||||
using Ocelot.Infrastructure.Claims.Parser;
|
using Ocelot.Infrastructure.Claims.Parser;
|
||||||
@ -44,14 +37,16 @@ namespace Ocelot.DependencyInjection
|
|||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using IdentityServer4.AccessTokenValidation;
|
using IdentityServer4.AccessTokenValidation;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Ocelot.Configuration;
|
using Ocelot.Configuration;
|
||||||
using Ocelot.Configuration.Builder;
|
|
||||||
using FileConfigurationProvider = Ocelot.Configuration.Provider.FileConfigurationProvider;
|
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using Butterfly.Client.AspNetCore;
|
using Butterfly.Client.AspNetCore;
|
||||||
|
using Ocelot.Infrastructure;
|
||||||
|
using Ocelot.Infrastructure.Consul;
|
||||||
|
using Butterfly.Client.Tracing;
|
||||||
|
using Ocelot.Middleware.Multiplexer;
|
||||||
|
using Pivotal.Discovery.Client;
|
||||||
|
using ServiceDiscovery.Providers;
|
||||||
|
|
||||||
public class OcelotBuilder : IOcelotBuilder
|
public class OcelotBuilder : IOcelotBuilder
|
||||||
{
|
{
|
||||||
@ -76,8 +71,8 @@ namespace Ocelot.DependencyInjection
|
|||||||
_services.TryAddSingleton<IHttpResponseHeaderReplacer, HttpResponseHeaderReplacer>();
|
_services.TryAddSingleton<IHttpResponseHeaderReplacer, HttpResponseHeaderReplacer>();
|
||||||
_services.TryAddSingleton<IHttpContextRequestHeaderReplacer, HttpContextRequestHeaderReplacer>();
|
_services.TryAddSingleton<IHttpContextRequestHeaderReplacer, HttpContextRequestHeaderReplacer>();
|
||||||
_services.TryAddSingleton<IHeaderFindAndReplaceCreator, HeaderFindAndReplaceCreator>();
|
_services.TryAddSingleton<IHeaderFindAndReplaceCreator, HeaderFindAndReplaceCreator>();
|
||||||
_services.TryAddSingleton<IOcelotConfigurationCreator, FileOcelotConfigurationCreator>();
|
_services.TryAddSingleton<IInternalConfigurationCreator, FileInternalConfigurationCreator>();
|
||||||
_services.TryAddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>();
|
_services.TryAddSingleton<IInternalConfigurationRepository, InMemoryInternalConfigurationRepository>();
|
||||||
_services.TryAddSingleton<IConfigurationValidator, FileConfigurationFluentValidator>();
|
_services.TryAddSingleton<IConfigurationValidator, FileConfigurationFluentValidator>();
|
||||||
_services.TryAddSingleton<IClaimsToThingCreator, ClaimsToThingCreator>();
|
_services.TryAddSingleton<IClaimsToThingCreator, ClaimsToThingCreator>();
|
||||||
_services.TryAddSingleton<IAuthenticationOptionsCreator, AuthenticationOptionsCreator>();
|
_services.TryAddSingleton<IAuthenticationOptionsCreator, AuthenticationOptionsCreator>();
|
||||||
@ -89,9 +84,8 @@ namespace Ocelot.DependencyInjection
|
|||||||
_services.TryAddSingleton<IRateLimitOptionsCreator, RateLimitOptionsCreator>();
|
_services.TryAddSingleton<IRateLimitOptionsCreator, RateLimitOptionsCreator>();
|
||||||
_services.TryAddSingleton<IBaseUrlFinder, BaseUrlFinder>();
|
_services.TryAddSingleton<IBaseUrlFinder, BaseUrlFinder>();
|
||||||
_services.TryAddSingleton<IRegionCreator, RegionCreator>();
|
_services.TryAddSingleton<IRegionCreator, RegionCreator>();
|
||||||
_services.TryAddSingleton<IFileConfigurationRepository, FileConfigurationRepository>();
|
_services.TryAddSingleton<IFileConfigurationRepository, DiskFileConfigurationRepository>();
|
||||||
_services.TryAddSingleton<IFileConfigurationSetter, FileConfigurationSetter>();
|
_services.TryAddSingleton<IFileConfigurationSetter, FileAndInternalConfigurationSetter>();
|
||||||
_services.TryAddSingleton<IFileConfigurationProvider, FileConfigurationProvider>();
|
|
||||||
_services.TryAddSingleton<IQosProviderHouse, QosProviderHouse>();
|
_services.TryAddSingleton<IQosProviderHouse, QosProviderHouse>();
|
||||||
_services.TryAddSingleton<IQoSProviderFactory, QoSProviderFactory>();
|
_services.TryAddSingleton<IQoSProviderFactory, QoSProviderFactory>();
|
||||||
_services.TryAddSingleton<IServiceDiscoveryProviderFactory, ServiceDiscoveryProviderFactory>();
|
_services.TryAddSingleton<IServiceDiscoveryProviderFactory, ServiceDiscoveryProviderFactory>();
|
||||||
@ -99,7 +93,6 @@ namespace Ocelot.DependencyInjection
|
|||||||
_services.TryAddSingleton<ILoadBalancerHouse, LoadBalancerHouse>();
|
_services.TryAddSingleton<ILoadBalancerHouse, LoadBalancerHouse>();
|
||||||
_services.TryAddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
|
_services.TryAddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
|
||||||
_services.TryAddSingleton<IRemoveOutputHeaders, RemoveOutputHeaders>();
|
_services.TryAddSingleton<IRemoveOutputHeaders, RemoveOutputHeaders>();
|
||||||
_services.TryAddSingleton<IOcelotConfigurationProvider, OcelotConfigurationProvider>();
|
|
||||||
_services.TryAddSingleton<IClaimToThingConfigurationParser, ClaimToThingConfigurationParser>();
|
_services.TryAddSingleton<IClaimToThingConfigurationParser, ClaimToThingConfigurationParser>();
|
||||||
_services.TryAddSingleton<IClaimsAuthoriser, ClaimsAuthoriser>();
|
_services.TryAddSingleton<IClaimsAuthoriser, ClaimsAuthoriser>();
|
||||||
_services.TryAddSingleton<IScopesAuthoriser, ScopesAuthoriser>();
|
_services.TryAddSingleton<IScopesAuthoriser, ScopesAuthoriser>();
|
||||||
@ -121,6 +114,17 @@ namespace Ocelot.DependencyInjection
|
|||||||
_services.TryAddSingleton<IDownstreamAddressesCreator, DownstreamAddressesCreator>();
|
_services.TryAddSingleton<IDownstreamAddressesCreator, DownstreamAddressesCreator>();
|
||||||
_services.TryAddSingleton<IDelegatingHandlerHandlerFactory, DelegatingHandlerHandlerFactory>();
|
_services.TryAddSingleton<IDelegatingHandlerHandlerFactory, DelegatingHandlerHandlerFactory>();
|
||||||
|
|
||||||
|
if (UsingEurekaServiceDiscoveryProvider(configurationRoot))
|
||||||
|
{
|
||||||
|
_services.AddDiscoveryClient(configurationRoot);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_services.TryAddSingleton<IDiscoveryClient, FakeEurekaDiscoveryClient>();
|
||||||
|
}
|
||||||
|
|
||||||
|
_services.TryAddSingleton<IHttpRequester, HttpClientHttpRequester>();
|
||||||
|
|
||||||
// see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc
|
// see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc
|
||||||
// could maybe use a scoped data repository
|
// could maybe use a scoped data repository
|
||||||
_services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
_services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||||
@ -148,6 +152,12 @@ namespace Ocelot.DependencyInjection
|
|||||||
|
|
||||||
// We add this here so that we can always inject something into the factory for IoC..
|
// We add this here so that we can always inject something into the factory for IoC..
|
||||||
_services.AddSingleton<IServiceTracer, FakeServiceTracer>();
|
_services.AddSingleton<IServiceTracer, FakeServiceTracer>();
|
||||||
|
_services.TryAddSingleton<IConsulPollerConfiguration, InMemoryConsulPollerConfiguration>();
|
||||||
|
_services.TryAddSingleton<IAddHeadersToResponse, AddHeadersToResponse>();
|
||||||
|
_services.TryAddSingleton<IPlaceholders, Placeholders>();
|
||||||
|
_services.TryAddSingleton<IConsulClientFactory, ConsulClientFactory>();
|
||||||
|
_services.TryAddSingleton<IResponseAggregatorFactory, InMemoryResponseAggregatorFactory>();
|
||||||
|
_services.TryAddSingleton<IDefinedAggregatorProvider, ServiceLocatorDefinedAggregatorProvider>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IOcelotAdministrationBuilder AddAdministration(string path, string secret)
|
public IOcelotAdministrationBuilder AddAdministration(string path, string secret)
|
||||||
@ -182,6 +192,20 @@ namespace Ocelot.DependencyInjection
|
|||||||
return new OcelotAdministrationBuilder(_services, _configurationRoot);
|
return new OcelotAdministrationBuilder(_services, _configurationRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IOcelotBuilder AddSingletonDefinedAggregator<T>()
|
||||||
|
where T : class, IDefinedAggregator
|
||||||
|
{
|
||||||
|
_services.AddSingleton<IDefinedAggregator, T>();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IOcelotBuilder AddTransientDefinedAggregator<T>()
|
||||||
|
where T : class, IDefinedAggregator
|
||||||
|
{
|
||||||
|
_services.AddTransient<IDefinedAggregator, T>();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public IOcelotBuilder AddSingletonDelegatingHandler<THandler>(bool global = false)
|
public IOcelotBuilder AddSingletonDelegatingHandler<THandler>(bool global = false)
|
||||||
where THandler : DelegatingHandler
|
where THandler : DelegatingHandler
|
||||||
{
|
{
|
||||||
@ -230,15 +254,6 @@ namespace Ocelot.DependencyInjection
|
|||||||
|
|
||||||
public IOcelotBuilder AddStoreOcelotConfigurationInConsul()
|
public IOcelotBuilder AddStoreOcelotConfigurationInConsul()
|
||||||
{
|
{
|
||||||
var serviceDiscoveryPort = _configurationRoot.GetValue("GlobalConfiguration:ServiceDiscoveryProvider:Port", 0);
|
|
||||||
var serviceDiscoveryHost = _configurationRoot.GetValue("GlobalConfiguration:ServiceDiscoveryProvider:Host", string.Empty);
|
|
||||||
|
|
||||||
var config = new ServiceProviderConfigurationBuilder()
|
|
||||||
.WithServiceDiscoveryProviderPort(serviceDiscoveryPort)
|
|
||||||
.WithServiceDiscoveryProviderHost(serviceDiscoveryHost)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
_services.AddSingleton<ServiceProviderConfiguration>(config);
|
|
||||||
_services.AddSingleton<ConsulFileConfigurationPoller>();
|
_services.AddSingleton<ConsulFileConfigurationPoller>();
|
||||||
_services.AddSingleton<IFileConfigurationRepository, ConsulFileConfigurationRepository>();
|
_services.AddSingleton<IFileConfigurationRepository, ConsulFileConfigurationRepository>();
|
||||||
return this;
|
return this;
|
||||||
@ -254,12 +269,12 @@ namespace Ocelot.DependencyInjection
|
|||||||
_services.AddSingleton<ICacheManager<CachedResponse>>(cacheManagerOutputCache);
|
_services.AddSingleton<ICacheManager<CachedResponse>>(cacheManagerOutputCache);
|
||||||
_services.AddSingleton<IOcelotCache<CachedResponse>>(ocelotOutputCacheManager);
|
_services.AddSingleton<IOcelotCache<CachedResponse>>(ocelotOutputCacheManager);
|
||||||
|
|
||||||
var ocelotConfigCacheManagerOutputCache = CacheFactory.Build<IOcelotConfiguration>("OcelotConfigurationCache", settings);
|
var ocelotConfigCacheManagerOutputCache = CacheFactory.Build<IInternalConfiguration>("OcelotConfigurationCache", settings);
|
||||||
var ocelotConfigCacheManager = new OcelotCacheManagerCache<IOcelotConfiguration>(ocelotConfigCacheManagerOutputCache);
|
var ocelotConfigCacheManager = new OcelotCacheManagerCache<IInternalConfiguration>(ocelotConfigCacheManagerOutputCache);
|
||||||
_services.RemoveAll(typeof(ICacheManager<IOcelotConfiguration>));
|
_services.RemoveAll(typeof(ICacheManager<IInternalConfiguration>));
|
||||||
_services.RemoveAll(typeof(IOcelotCache<IOcelotConfiguration>));
|
_services.RemoveAll(typeof(IOcelotCache<IInternalConfiguration>));
|
||||||
_services.AddSingleton<ICacheManager<IOcelotConfiguration>>(ocelotConfigCacheManagerOutputCache);
|
_services.AddSingleton<ICacheManager<IInternalConfiguration>>(ocelotConfigCacheManagerOutputCache);
|
||||||
_services.AddSingleton<IOcelotCache<IOcelotConfiguration>>(ocelotConfigCacheManager);
|
_services.AddSingleton<IOcelotCache<IInternalConfiguration>>(ocelotConfigCacheManager);
|
||||||
|
|
||||||
var fileConfigCacheManagerOutputCache = CacheFactory.Build<FileConfiguration>("FileConfigurationCache", settings);
|
var fileConfigCacheManagerOutputCache = CacheFactory.Build<FileConfiguration>("FileConfigurationCache", settings);
|
||||||
var fileConfigCacheManager = new OcelotCacheManagerCache<FileConfiguration>(fileConfigCacheManagerOutputCache);
|
var fileConfigCacheManager = new OcelotCacheManagerCache<FileConfiguration>(fileConfigCacheManagerOutputCache);
|
||||||
@ -280,7 +295,6 @@ namespace Ocelot.DependencyInjection
|
|||||||
private void AddIdentityServer(IIdentityServerConfiguration identityServerConfiguration, IAdministrationPath adminPath)
|
private void AddIdentityServer(IIdentityServerConfiguration identityServerConfiguration, IAdministrationPath adminPath)
|
||||||
{
|
{
|
||||||
_services.TryAddSingleton<IIdentityServerConfiguration>(identityServerConfiguration);
|
_services.TryAddSingleton<IIdentityServerConfiguration>(identityServerConfiguration);
|
||||||
_services.TryAddSingleton<IHashMatcher, HashMatcher>();
|
|
||||||
var identityServerBuilder = _services
|
var identityServerBuilder = _services
|
||||||
.AddIdentityServer(o => {
|
.AddIdentityServer(o => {
|
||||||
o.IssuerUri = "Ocelot";
|
o.IssuerUri = "Ocelot";
|
||||||
@ -345,5 +359,13 @@ namespace Ocelot.DependencyInjection
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool UsingEurekaServiceDiscoveryProvider(IConfiguration configurationRoot)
|
||||||
|
{
|
||||||
|
var type = configurationRoot.GetValue<string>("GlobalConfiguration:ServiceDiscoveryProvider:Type",
|
||||||
|
string.Empty);
|
||||||
|
|
||||||
|
return type.ToLower() == "eureka";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ namespace Ocelot.DownstreamRouteFinder.Finder
|
|||||||
_placeholderNameAndValueFinder = urlPathPlaceholderNameAndValueFinder;
|
_placeholderNameAndValueFinder = urlPathPlaceholderNameAndValueFinder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response<DownstreamRoute> FindDownstreamRoute(string path, string httpMethod, IOcelotConfiguration configuration, string upstreamHost)
|
public Response<DownstreamRoute> FindDownstreamRoute(string path, string httpMethod, IInternalConfiguration configuration, string upstreamHost)
|
||||||
{
|
{
|
||||||
var downstreamRoutes = new List<DownstreamRoute>();
|
var downstreamRoutes = new List<DownstreamRoute>();
|
||||||
|
|
||||||
@ -44,10 +44,7 @@ namespace Ocelot.DownstreamRouteFinder.Finder
|
|||||||
return notNullOption != null ? new OkResponse<DownstreamRoute>(notNullOption) : new OkResponse<DownstreamRoute>(nullOption);
|
return notNullOption != null ? new OkResponse<DownstreamRoute>(notNullOption) : new OkResponse<DownstreamRoute>(nullOption);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ErrorResponse<DownstreamRoute>(new List<Error>
|
return new ErrorResponse<DownstreamRoute>(new UnableToFindDownstreamRouteError(path, httpMethod));
|
||||||
{
|
|
||||||
new UnableToFindDownstreamRouteError()
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool RouteIsApplicableToThisRequest(ReRoute reRoute, string httpMethod, string upstreamHost)
|
private bool RouteIsApplicableToThisRequest(ReRoute reRoute, string httpMethod, string upstreamHost)
|
||||||
|
@ -6,6 +6,6 @@ namespace Ocelot.DownstreamRouteFinder.Finder
|
|||||||
{
|
{
|
||||||
public interface IDownstreamRouteFinder
|
public interface IDownstreamRouteFinder
|
||||||
{
|
{
|
||||||
Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod, IOcelotConfiguration configuration, string upstreamHost);
|
Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod, IInternalConfiguration configuration, string upstreamHost);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,8 @@ namespace Ocelot.DownstreamRouteFinder.Finder
|
|||||||
{
|
{
|
||||||
public class UnableToFindDownstreamRouteError : Error
|
public class UnableToFindDownstreamRouteError : Error
|
||||||
{
|
{
|
||||||
public UnableToFindDownstreamRouteError() : base("UnableToFindDownstreamRouteError", OcelotErrorCode.UnableToFindDownstreamRouteError)
|
public UnableToFindDownstreamRouteError(string path, string httpVerb)
|
||||||
|
: base($"Unable to find downstream route for path: {path}, verb: {httpVerb}", OcelotErrorCode.UnableToFindDownstreamRouteError)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Ocelot.Configuration;
|
using System.Linq;
|
||||||
using Ocelot.Configuration.Provider;
|
using Ocelot.Configuration.Repository;
|
||||||
using Ocelot.DownstreamRouteFinder.Finder;
|
using Ocelot.DownstreamRouteFinder.Finder;
|
||||||
using Ocelot.Infrastructure.Extensions;
|
using Ocelot.Infrastructure.Extensions;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
@ -13,21 +13,20 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
|
|||||||
{
|
{
|
||||||
private readonly OcelotRequestDelegate _next;
|
private readonly OcelotRequestDelegate _next;
|
||||||
private readonly IDownstreamRouteFinder _downstreamRouteFinder;
|
private readonly IDownstreamRouteFinder _downstreamRouteFinder;
|
||||||
private readonly IOcelotLogger _logger;
|
private readonly IInternalConfigurationRepository _repo;
|
||||||
private readonly IOcelotConfigurationProvider _configProvider;
|
|
||||||
private readonly IMultiplexer _multiplexer;
|
private readonly IMultiplexer _multiplexer;
|
||||||
|
|
||||||
public DownstreamRouteFinderMiddleware(OcelotRequestDelegate next,
|
public DownstreamRouteFinderMiddleware(OcelotRequestDelegate next,
|
||||||
IOcelotLoggerFactory loggerFactory,
|
IOcelotLoggerFactory loggerFactory,
|
||||||
IDownstreamRouteFinder downstreamRouteFinder,
|
IDownstreamRouteFinder downstreamRouteFinder,
|
||||||
IOcelotConfigurationProvider configProvider,
|
IInternalConfigurationRepository repo,
|
||||||
IMultiplexer multiplexer)
|
IMultiplexer multiplexer)
|
||||||
|
:base(loggerFactory.CreateLogger<DownstreamRouteFinderMiddleware>())
|
||||||
{
|
{
|
||||||
_configProvider = configProvider;
|
_repo = repo;
|
||||||
_multiplexer = multiplexer;
|
_multiplexer = multiplexer;
|
||||||
_next = next;
|
_next = next;
|
||||||
_downstreamRouteFinder = downstreamRouteFinder;
|
_downstreamRouteFinder = downstreamRouteFinder;
|
||||||
_logger = loggerFactory.CreateLogger<DownstreamRouteFinderMiddleware>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Invoke(DownstreamContext context)
|
public async Task Invoke(DownstreamContext context)
|
||||||
@ -36,31 +35,31 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
|
|||||||
|
|
||||||
var upstreamHost = context.HttpContext.Request.Headers["Host"];
|
var upstreamHost = context.HttpContext.Request.Headers["Host"];
|
||||||
|
|
||||||
var configuration = await _configProvider.Get();
|
var configuration = _repo.Get();
|
||||||
|
|
||||||
if (configuration.IsError)
|
if (configuration.IsError)
|
||||||
{
|
{
|
||||||
_logger.LogError($"{MiddlewareName} setting pipeline errors. IOcelotConfigurationProvider returned {configuration.Errors.ToErrorString()}");
|
Logger.LogWarning($"{MiddlewareName} setting pipeline errors. IOcelotConfigurationProvider returned {configuration.Errors.ToErrorString()}");
|
||||||
SetPipelineError(context, configuration.Errors);
|
SetPipelineError(context, configuration.Errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.ServiceProviderConfiguration = configuration.Data.ServiceProviderConfiguration;
|
context.ServiceProviderConfiguration = configuration.Data.ServiceProviderConfiguration;
|
||||||
|
|
||||||
_logger.LogDebug("upstream url path is {upstreamUrlPath}", upstreamUrlPath);
|
Logger.LogDebug($"Upstream url path is {upstreamUrlPath}");
|
||||||
|
|
||||||
var downstreamRoute = _downstreamRouteFinder.FindDownstreamRoute(upstreamUrlPath, context.HttpContext.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.LogWarning($"{MiddlewareName} setting pipeline errors. IDownstreamRouteFinder returned {downstreamRoute.Errors.ToErrorString()}");
|
||||||
|
|
||||||
SetPipelineError(context, downstreamRoute.Errors);
|
SetPipelineError(context, downstreamRoute.Errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo - put this back in
|
var downstreamPathTemplates = string.Join(", ", downstreamRoute.Data.ReRoute.DownstreamReRoute.Select(r => r.DownstreamPathTemplate.Value));
|
||||||
//// _logger.LogDebug("downstream template is {downstreamRoute.Data.ReRoute.DownstreamPath}", downstreamRoute.Data.ReRoute.DownstreamReRoute.DownstreamPathTemplate);
|
Logger.LogDebug($"downstream templates are {downstreamPathTemplates}");
|
||||||
|
|
||||||
context.TemplatePlaceholderNameAndValues = downstreamRoute.Data.TemplatePlaceholderNameAndValues;
|
context.TemplatePlaceholderNameAndValues = downstreamRoute.Data.TemplatePlaceholderNameAndValues;
|
||||||
|
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
using Ocelot.Errors;
|
|
||||||
|
|
||||||
namespace Ocelot.DownstreamUrlCreator
|
|
||||||
{
|
|
||||||
public class DownstreamHostNullOrEmptyError : Error
|
|
||||||
{
|
|
||||||
public DownstreamHostNullOrEmptyError()
|
|
||||||
: base("downstream host was null or empty", OcelotErrorCode.DownstreamHostNullOrEmptyError)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
using Ocelot.Errors;
|
|
||||||
|
|
||||||
namespace Ocelot.DownstreamUrlCreator
|
|
||||||
{
|
|
||||||
public class DownstreamPathNullOrEmptyError : Error
|
|
||||||
{
|
|
||||||
public DownstreamPathNullOrEmptyError()
|
|
||||||
: base("downstream path was null or empty", OcelotErrorCode.DownstreamPathNullOrEmptyError)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
using Ocelot.Errors;
|
|
||||||
|
|
||||||
namespace Ocelot.DownstreamUrlCreator
|
|
||||||
{
|
|
||||||
public class DownstreamSchemeNullOrEmptyError : Error
|
|
||||||
{
|
|
||||||
public DownstreamSchemeNullOrEmptyError()
|
|
||||||
: base("downstream scheme was null or empty", OcelotErrorCode.DownstreamSchemeNullOrEmptyError)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
/*
|
|
||||||
using Ocelot.Responses;
|
|
||||||
using Ocelot.Values;
|
|
||||||
|
|
||||||
namespace Ocelot.DownstreamUrlCreator
|
|
||||||
{
|
|
||||||
public interface IUrlBuilder
|
|
||||||
{
|
|
||||||
Response<DownstreamUrl> Build(string downstreamPath, string downstreamScheme, ServiceHostAndPort downstreamHostAndPort);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
@ -16,15 +16,14 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
|
|||||||
{
|
{
|
||||||
private readonly OcelotRequestDelegate _next;
|
private readonly OcelotRequestDelegate _next;
|
||||||
private readonly IDownstreamPathPlaceholderReplacer _replacer;
|
private readonly IDownstreamPathPlaceholderReplacer _replacer;
|
||||||
private readonly IOcelotLogger _logger;
|
|
||||||
|
|
||||||
public DownstreamUrlCreatorMiddleware(OcelotRequestDelegate next,
|
public DownstreamUrlCreatorMiddleware(OcelotRequestDelegate next,
|
||||||
IOcelotLoggerFactory loggerFactory,
|
IOcelotLoggerFactory loggerFactory,
|
||||||
IDownstreamPathPlaceholderReplacer replacer)
|
IDownstreamPathPlaceholderReplacer replacer)
|
||||||
|
:base(loggerFactory.CreateLogger<DownstreamUrlCreatorMiddleware>())
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_replacer = replacer;
|
_replacer = replacer;
|
||||||
_logger = loggerFactory.CreateLogger<DownstreamUrlCreatorMiddleware>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Invoke(DownstreamContext context)
|
public async Task Invoke(DownstreamContext context)
|
||||||
@ -34,55 +33,42 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
|
|||||||
|
|
||||||
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(context, dsPath.Errors);
|
SetPipelineError(context, dsPath.Errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
UriBuilder uriBuilder;
|
context.DownstreamRequest.Scheme = context.DownstreamReRoute.DownstreamScheme;
|
||||||
|
|
||||||
if (ServiceFabricRequest(context))
|
if (ServiceFabricRequest(context))
|
||||||
{
|
{
|
||||||
uriBuilder = CreateServiceFabricUri(context, dsPath);
|
var pathAndQuery = CreateServiceFabricUri(context, dsPath);
|
||||||
|
context.DownstreamRequest.AbsolutePath = pathAndQuery.path;
|
||||||
|
context.DownstreamRequest.Query = pathAndQuery.query;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
uriBuilder = new UriBuilder(context.DownstreamRequest.RequestUri)
|
context.DownstreamRequest.AbsolutePath = dsPath.Data.Value;
|
||||||
{
|
|
||||||
Path = dsPath.Data.Value,
|
|
||||||
Scheme = context.DownstreamReRoute.DownstreamScheme
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
context.DownstreamRequest.RequestUri = uriBuilder.Uri;
|
Logger.LogDebug($"Downstream url is {context.DownstreamRequest}");
|
||||||
|
|
||||||
_logger.LogDebug("downstream url is {downstreamUrl.Data.Value}", context.DownstreamRequest.RequestUri);
|
|
||||||
|
|
||||||
await _next.Invoke(context);
|
await _next.Invoke(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
private UriBuilder CreateServiceFabricUri(DownstreamContext context, Response<DownstreamPath> dsPath)
|
private (string path, string query) CreateServiceFabricUri(DownstreamContext context, Response<DownstreamPath> dsPath)
|
||||||
{
|
{
|
||||||
var query = context.DownstreamRequest.RequestUri.Query;
|
var query = context.DownstreamRequest.Query;
|
||||||
var scheme = context.DownstreamReRoute.DownstreamScheme;
|
|
||||||
var host = context.DownstreamRequest.RequestUri.Host;
|
|
||||||
var port = context.DownstreamRequest.RequestUri.Port;
|
|
||||||
var serviceFabricPath = $"/{context.DownstreamReRoute.ServiceName + dsPath.Data.Value}";
|
var serviceFabricPath = $"/{context.DownstreamReRoute.ServiceName + dsPath.Data.Value}";
|
||||||
|
|
||||||
Uri uri;
|
|
||||||
|
|
||||||
if (RequestForStatefullService(query))
|
if (RequestForStatefullService(query))
|
||||||
{
|
{
|
||||||
uri = new Uri($"{scheme}://{host}:{port}{serviceFabricPath}{query}");
|
return (serviceFabricPath, query);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var split = string.IsNullOrEmpty(query) ? "?" : "&";
|
|
||||||
uri = new Uri($"{scheme}://{host}:{port}{serviceFabricPath}{query}{split}cmd=instance");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new UriBuilder(uri);
|
var split = string.IsNullOrEmpty(query) ? "?" : "&";
|
||||||
|
return (serviceFabricPath, $"{query}{split}cmd=instance");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool ServiceFabricRequest(DownstreamContext context)
|
private static bool ServiceFabricRequest(DownstreamContext context)
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Ocelot.Errors;
|
|
||||||
using Ocelot.Responses;
|
|
||||||
using Ocelot.Values;
|
|
||||||
|
|
||||||
namespace Ocelot.DownstreamUrlCreator
|
|
||||||
{
|
|
||||||
public class UrlBuilder : IUrlBuilder
|
|
||||||
{
|
|
||||||
public Response<DownstreamUrl> Build(string downstreamPath, string downstreamScheme, ServiceHostAndPort downstreamHostAndPort)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(downstreamPath))
|
|
||||||
{
|
|
||||||
return new ErrorResponse<DownstreamUrl>(new List<Error> {new DownstreamPathNullOrEmptyError()});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(downstreamScheme))
|
|
||||||
{
|
|
||||||
return new ErrorResponse<DownstreamUrl>(new List<Error> { new DownstreamSchemeNullOrEmptyError() });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(downstreamHostAndPort.DownstreamHost))
|
|
||||||
{
|
|
||||||
return new ErrorResponse<DownstreamUrl>(new List<Error> { new DownstreamHostNullOrEmptyError() });
|
|
||||||
}
|
|
||||||
|
|
||||||
var builder = new UriBuilder
|
|
||||||
{
|
|
||||||
Host = downstreamHostAndPort.DownstreamHost,
|
|
||||||
Path = downstreamPath,
|
|
||||||
Scheme = downstreamScheme,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (downstreamHostAndPort.DownstreamPort > 0)
|
|
||||||
{
|
|
||||||
builder.Port = downstreamHostAndPort.DownstreamPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
var url = builder.Uri.ToString();
|
|
||||||
|
|
||||||
return new OkResponse<DownstreamUrl>(new DownstreamUrl(url));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
@ -1,10 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Ocelot.Configuration.Repository;
|
||||||
using Microsoft.Extensions.Primitives;
|
|
||||||
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;
|
||||||
@ -18,68 +15,64 @@ namespace Ocelot.Errors.Middleware
|
|||||||
public class ExceptionHandlerMiddleware : OcelotMiddleware
|
public class ExceptionHandlerMiddleware : OcelotMiddleware
|
||||||
{
|
{
|
||||||
private readonly OcelotRequestDelegate _next;
|
private readonly OcelotRequestDelegate _next;
|
||||||
private readonly IOcelotLogger _logger;
|
private readonly IInternalConfigurationRepository _configRepo;
|
||||||
private readonly IOcelotConfigurationProvider _provider;
|
|
||||||
private readonly IRequestScopedDataRepository _repo;
|
private readonly IRequestScopedDataRepository _repo;
|
||||||
|
|
||||||
public ExceptionHandlerMiddleware(OcelotRequestDelegate next,
|
public ExceptionHandlerMiddleware(OcelotRequestDelegate next,
|
||||||
IOcelotLoggerFactory loggerFactory,
|
IOcelotLoggerFactory loggerFactory,
|
||||||
IOcelotConfigurationProvider provider,
|
IInternalConfigurationRepository configRepo,
|
||||||
IRequestScopedDataRepository repo)
|
IRequestScopedDataRepository repo)
|
||||||
|
: base(loggerFactory.CreateLogger<ExceptionHandlerMiddleware>())
|
||||||
{
|
{
|
||||||
_provider = provider;
|
_configRepo = configRepo;
|
||||||
_repo = repo;
|
_repo = repo;
|
||||||
_next = next;
|
_next = next;
|
||||||
_logger = loggerFactory.CreateLogger<ExceptionHandlerMiddleware>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Invoke(DownstreamContext context)
|
public async Task Invoke(DownstreamContext context)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await TrySetGlobalRequestId(context);
|
TrySetGlobalRequestId(context);
|
||||||
|
|
||||||
_logger.LogDebug("ocelot pipeline started");
|
Logger.LogDebug("ocelot pipeline started");
|
||||||
|
|
||||||
await _next.Invoke(context);
|
await _next.Invoke(context);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("error calling middleware");
|
Logger.LogDebug("error calling middleware");
|
||||||
|
|
||||||
var message = CreateMessage(context, e);
|
var message = CreateMessage(context, e);
|
||||||
|
|
||||||
_logger.LogError(message, e);
|
Logger.LogError(message, e);
|
||||||
|
|
||||||
SetInternalServerErrorOnResponse(context);
|
SetInternalServerErrorOnResponse(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogDebug("ocelot pipeline finished");
|
Logger.LogDebug("ocelot pipeline finished");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task TrySetGlobalRequestId(DownstreamContext context)
|
private void 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 _provider.Get();
|
var configuration = _configRepo.Get();
|
||||||
|
|
||||||
//if error throw to catch below..
|
if(configuration.IsError)
|
||||||
if(configuration.IsError)
|
{
|
||||||
{
|
throw new Exception($"{MiddlewareName} setting pipeline errors. IOcelotConfigurationProvider returned {configuration.Errors.ToErrorString()}");
|
||||||
throw new Exception($"{MiddlewareName} setting pipeline errors. IOcelotConfigurationProvider returned {configuration.Errors.ToErrorString()}");
|
|
||||||
}
|
|
||||||
|
|
||||||
//else set the request id?
|
|
||||||
var key = configuration.Data.RequestId;
|
|
||||||
|
|
||||||
StringValues upstreamRequestIds;
|
|
||||||
if (!string.IsNullOrEmpty(key) && context.HttpContext.Request.Headers.TryGetValue(key, out upstreamRequestIds))
|
|
||||||
{
|
|
||||||
//todo fix looking in both places
|
|
||||||
context.HttpContext.TraceIdentifier = upstreamRequestIds.First();
|
|
||||||
_repo.Add<string>("RequestId", context.HttpContext.TraceIdentifier);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var key = configuration.Data.RequestId;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(key) && context.HttpContext.Request.Headers.TryGetValue(key, out var upstreamRequestIds))
|
||||||
|
{
|
||||||
|
context.HttpContext.TraceIdentifier = upstreamRequestIds.First();
|
||||||
|
}
|
||||||
|
|
||||||
|
_repo.Add("RequestId", context.HttpContext.TraceIdentifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetInternalServerErrorOnResponse(DownstreamContext context)
|
private void SetInternalServerErrorOnResponse(DownstreamContext context)
|
||||||
|
@ -34,6 +34,8 @@
|
|||||||
RateLimitOptionsError,
|
RateLimitOptionsError,
|
||||||
PathTemplateDoesntStartWithForwardSlash,
|
PathTemplateDoesntStartWithForwardSlash,
|
||||||
FileValidationFailedError,
|
FileValidationFailedError,
|
||||||
UnableToFindDelegatingHandlerProviderError
|
UnableToFindDelegatingHandlerProviderError,
|
||||||
|
CouldNotFindPlaceholderError,
|
||||||
|
CouldNotFindAggregatorError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,9 @@ using Ocelot.Configuration;
|
|||||||
using Ocelot.Infrastructure.Claims.Parser;
|
using Ocelot.Infrastructure.Claims.Parser;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Ocelot.Configuration.Creator;
|
||||||
|
using Ocelot.Request.Middleware;
|
||||||
|
|
||||||
namespace Ocelot.Headers
|
namespace Ocelot.Headers
|
||||||
{
|
{
|
||||||
@ -16,7 +19,7 @@ namespace Ocelot.Headers
|
|||||||
_claimsParser = claimsParser;
|
_claimsParser = claimsParser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response SetHeadersOnDownstreamRequest(List<ClaimToThing> claimsToThings, IEnumerable<System.Security.Claims.Claim> claims, HttpRequestMessage downstreamRequest)
|
public Response SetHeadersOnDownstreamRequest(List<ClaimToThing> claimsToThings, IEnumerable<System.Security.Claims.Claim> claims, DownstreamRequest downstreamRequest)
|
||||||
{
|
{
|
||||||
foreach (var config in claimsToThings)
|
foreach (var config in claimsToThings)
|
||||||
{
|
{
|
||||||
@ -39,5 +42,19 @@ namespace Ocelot.Headers
|
|||||||
|
|
||||||
return new OkResponse();
|
return new OkResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetHeadersOnDownstreamRequest(IEnumerable<AddHeader> headers, HttpContext context)
|
||||||
|
{
|
||||||
|
var requestHeader = context.Request.Headers;
|
||||||
|
foreach (var header in headers)
|
||||||
|
{
|
||||||
|
if (requestHeader.ContainsKey(header.Key))
|
||||||
|
{
|
||||||
|
requestHeader.Remove(header.Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
requestHeader.Add(header.Key, header.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
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