diff --git a/.gitignore b/.gitignore
index 836e0bfe..476938f0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -243,7 +243,7 @@ tools/
.DS_Store
# Ocelot acceptance test config
-test/Ocelot.AcceptanceTests/configuration.json
+test/Ocelot.AcceptanceTests/ocelot.json
# Read the docstates
_build/
@@ -251,4 +251,4 @@ _static/
_templates/
# JetBrains Rider
-.idea/
\ No newline at end of file
+.idea/
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..ef0701aa
--- /dev/null
+++ b/.travis.yml
@@ -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
\ No newline at end of file
diff --git a/README.md b/README.md
index 6bbc77e0..edd28eaf 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,11 @@
[
](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://coveralls.io/github/TomPallister/Ocelot?branch=develop)
+[](https://coveralls.io/github/ThreeMammals/Ocelot?branch=develop)
# Ocelot
@@ -38,8 +39,9 @@ A quick list of Ocelot's capabilities for more information see the [documentatio
* Routing
* Request Aggregation
-* Service Discovery with Consul
+* Service Discovery with Consul & Eureka
* Service Fabric
+* WebSockets
* Authentication
* Authorisation
* Rate Limiting
@@ -50,6 +52,7 @@ A quick list of Ocelot's capabilities for more information see the [documentatio
* Headers / Query String / Claims Transformation
* Custom Middleware / Delegating Handlers
* Configuration / Administration REST API
+* Platform / Cloud agnostic
## How to install
diff --git a/build.cake b/build.cake
index af2f9364..f757228c 100644
--- a/build.cake
+++ b/build.cake
@@ -17,7 +17,7 @@ var artifactsDir = Directory("artifacts");
// unit testing
var artifactsForUnitTestsDir = artifactsDir + Directory("UnitTests");
var unitTestAssemblies = @"./test/Ocelot.UnitTests/Ocelot.UnitTests.csproj";
-var minCodeCoverage = 76.4d;
+var minCodeCoverage = 82d;
var coverallsRepoToken = "coveralls-repo-token-ocelot";
var coverallsRepo = "https://coveralls.io/github/TomPallister/Ocelot";
@@ -189,6 +189,24 @@ Task("RunAcceptanceTests")
.IsDependentOn("Compile")
.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
{
Configuration = compileConfig,
@@ -205,6 +223,24 @@ Task("RunIntegrationTests")
.IsDependentOn("Compile")
.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
{
Configuration = compileConfig,
diff --git a/build.sh b/build.sh
index 04731adf..5b3d6020 100755
--- a/build.sh
+++ b/build.sh
@@ -98,4 +98,4 @@ if $SHOW_VERSION; then
exec mono "$CAKE_EXE" -version
else
exec mono "$CAKE_EXE" $SCRIPT -verbosity=$VERBOSITY -configuration=$CONFIGURATION -target=$TARGET $DRYRUN "${SCRIPT_ARGUMENTS[@]}"
-fi
\ No newline at end of file
+fi
diff --git a/docs/features/configuration.rst b/docs/features/configuration.rst
index 93cae138..4e11e7ed 100644
--- a/docs/features/configuration.rst
+++ b/docs/features/configuration.rst
@@ -1,7 +1,7 @@
Configuration
============
-An example configuration can be found `here `_.
+An example configuration can be found `here `_.
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
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,
"UseTracing": true
},
- "UseServiceDiscovery": false
+ "UseServiceDiscovery": false,
+ "DangerousAcceptAnyServerCertificateValidator": false
}
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
^^^^^^^^^^^^^^^^^^^^^
@@ -92,15 +84,40 @@ to you
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
- .AddJsonFile("configuration.json")
+ .AddJsonFile("ocelot.json")
.AddJsonFile($"configuration.{hostingContext.HostingEnvironment.EnvironmentName}.json")
.AddEnvironmentVariables();
})
-Ocelot should now use the environment specific configuration and fall back to configuration.json if there isnt one.
+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 `_.
+Merging configuration files
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This feature was requested in `Issue 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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -112,7 +129,7 @@ If you add the following when you register your services Ocelot will attempt to
.AddOcelot()
.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.
.. code-block:: json
@@ -127,4 +144,31 @@ finds your Consul agent and interacts to load and store the configuration from C
I decided to create this feature after working on the raft consensus algorithm and finding out its super hard. Why not take advantage of the fact Consul already gives you this!
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.
\ No newline at end of file
+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 `_ 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.
\ No newline at end of file
diff --git a/docs/features/delegatinghandlers.rst b/docs/features/delegatinghandlers.rst
index 80dfa16b..445a46fb 100644
--- a/docs/features/delegatinghandlers.rst
+++ b/docs/features/delegatinghandlers.rst
@@ -40,7 +40,7 @@ Or transient as below...
.AddTransientDelegatingHandler()
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.
e.g.
@@ -58,7 +58,7 @@ Or transient as below...
.AddTransientDelegatingHandler(true)
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.
.. 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:
-1. Any globals that are left in the order they were added to services and are not in the DelegatingHandlers array from configuration.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.
+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 ocelot.json ordered as they are in the DelegatingHandlers array.
3. Tracing DelegatingHandler if enabled (see tracing docs).
4. QoS DelegatingHandler if enabled (see QoS docs).
5. The HttpClient sends the HttpRequestMessage.
diff --git a/docs/features/graphql.rst b/docs/features/graphql.rst
new file mode 100644
index 00000000..1b527314
--- /dev/null
+++ b/docs/features/graphql.rst
@@ -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 `_ library.
+
+
+Please see the sample project `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 :>
+
+
diff --git a/docs/features/headerstransformation.rst b/docs/features/headerstransformation.rst
index 5063c1b7..744227e2 100644
--- a/docs/features/headerstransformation.rst
+++ b/docs/features/headerstransformation.rst
@@ -1,10 +1,50 @@
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 `_ and I decided that it was going to be useful in various ways.
-Syntax
-^^^^^^
+Add to Request
+^^^^^^^^^^^^^^
+
+This feature was requestes in `GitHub #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 `_.
+
+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.
@@ -17,7 +57,7 @@ The key is "Test" and the value is "http://www.bbc.co.uk/, http://ocelot.com/".
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
@@ -26,9 +66,9 @@ Add the following to a ReRoute in configuration.json in order to replace http://
},
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
@@ -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.
{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
^^^^^^^^^^^^^^^^^^^^^^
diff --git a/docs/features/loadbalancer.rst b/docs/features/loadbalancer.rst
index f587aa22..c2cc0f26 100644
--- a/docs/features/loadbalancer.rst
+++ b/docs/features/loadbalancer.rst
@@ -16,7 +16,7 @@ You must choose in your configuration which load balancer to use.
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
diff --git a/docs/features/logging.rst b/docs/features/logging.rst
index 313f62ff..b09a26cf 100644
--- a/docs/features/logging.rst
+++ b/docs/features/logging.rst
@@ -11,4 +11,10 @@ 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
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.
\ No newline at end of file
+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 :)
diff --git a/docs/features/middlewareinjection.rst b/docs/features/middlewareinjection.rst
index f25cfa26..44f44fb3 100644
--- a/docs/features/middlewareinjection.rst
+++ b/docs/features/middlewareinjection.rst
@@ -9,7 +9,7 @@ and override middleware. This is done as follos.
.. code-block:: csharp
- var configuration = new OcelotMiddlewareConfiguration
+ var configuration = new OcelotPipelineConfiguration
{
PreErrorResponderMiddleware = async (ctx, next) =>
{
@@ -38,4 +38,4 @@ The user can set functions against the following.
* PreQueryStringBuilderMiddleware - This alows the user to manipulate the query string on the http request before it is passed to Ocelots request creator.
Obviously you can just add middleware as normal before the call to app.UseOcelot() It cannot be added
-after as Ocelot does not call the next middleware.
\ No newline at end of file
+after as Ocelot does not call the next middleware.
diff --git a/docs/features/qualityofservice.rst b/docs/features/qualityofservice.rst
index 9eebf923..17bf373d 100644
--- a/docs/features/qualityofservice.rst
+++ b/docs/features/qualityofservice.rst
@@ -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
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.
\ No newline at end of file
+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.
\ No newline at end of file
diff --git a/docs/features/ratelimiting.rst b/docs/features/ratelimiting.rst
index d929c905..7f9c0768 100644
--- a/docs/features/ratelimiting.rst
+++ b/docs/features/ratelimiting.rst
@@ -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.
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
diff --git a/docs/features/requestaggregation.rst b/docs/features/requestaggregation.rst
index b359eb72..a5ca30f6 100644
--- a/docs/features/requestaggregation.rst
+++ b/docs/features/requestaggregation.rst
@@ -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
architecture with Ocelot.
-This feature was requested as part of `Issue 79 `_ .
+This feature was requested as part of `Issue 79 `_ and further improvements were made as part of `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.
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();
+
+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();
+
+ services
+ .AddOcelot()
+ .AddSingletonDefinedAggregator();
+
+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();
+
+In order to make an Aggregator you must implement this interface.
+
+.. code-block:: csharp
+
+ public interface IDefinedAggregator
+ {
+ Task Aggregate(List 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
{
@@ -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}}
-Gotcha's / Further info
-^^^^^^^^^^^^^^^^^^^^^^^
-
At the moment the aggregation is very simple. Ocelot just gets the response from your downstream service and sticks it into a json dictionary
as above. With the ReRoute key being the key of the dictionary and the value the response body from your downstream service. You can see that the object is just
JSON without any pretty spaces etc.
@@ -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.
+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.
Aggregation only supports the GET HTTP Verb.
-If you downstream services return a 404 the aggregate will just return nothing for that downstream service.
-It will not change the aggregate response into a 404 even if all the downstreams return a 404.
-
-Future
-^^^^^^
-
-There are loads of cool ways to enchance this such as..
-
-What happens when downstream goes slow..should we timeout?
-Can we do something like GraphQL where the user chooses what fields are returned?
-Can we handle 404 better etc?
-Can we make this not just support a JSON dictionary response?
-
diff --git a/docs/features/requestid.rst b/docs/features/requestid.rst
index 6e4f239b..37752eda 100644
--- a/docs/features/requestid.rst
+++ b/docs/features/requestid.rst
@@ -12,7 +12,7 @@ In order to use the reques tid feature you have two options.
*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
@@ -24,7 +24,7 @@ I reccomend using the GlobalConfiguration unless you really need it to be ReRout
*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
diff --git a/docs/features/routing.rst b/docs/features/routing.rst
index 4215aa44..664021a6 100644
--- a/docs/features/routing.rst
+++ b/docs/features/routing.rst
@@ -139,4 +139,42 @@ The ReRoute above will only be matched when the host header value is somedomain.
If you do not set UpstreamHost on a ReRoue then any host header can match it. This is basically a catch all and
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 `_ .
\ No newline at end of file
+This feature was requested as part of `Issue 216 `_ .
+
+Priority
+^^^^^^^^
+
+In `Issue 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!).
\ No newline at end of file
diff --git a/docs/features/servicediscovery.rst b/docs/features/servicediscovery.rst
index f8cadd2e..6fe25c12 100644
--- a/docs/features/servicediscovery.rst
+++ b/docs/features/servicediscovery.rst
@@ -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.
+
+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 `_ . to add support for Netflix's
+Eureka service discovery provider. The main reason for this is it is a key part of `Steeltoe `_ which is something
+to do with `Pivotal `_! 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 `_ 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.
+
diff --git a/docs/features/tracing.rst b/docs/features/tracing.rst
index 0a896d45..aa4b1fd3 100644
--- a/docs/features/tracing.rst
+++ b/docs/features/tracing.rst
@@ -20,7 +20,7 @@ In your ConfigureServices method
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
diff --git a/docs/features/websockets.rst b/docs/features/websockets.rst
new file mode 100644
index 00000000..624f42a9
--- /dev/null
+++ b/docs/features/websockets.rst
@@ -0,0 +1,68 @@
+Websockets
+==========
+
+Ocelot supports proxying websockets with some extra bits. This functionality was requested in `Issue 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!
+
+
diff --git a/docs/index.rst b/docs/index.rst
index 43a6c436..7989fcdd 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -21,10 +21,12 @@ Thanks for taking a look at the Ocelot documentation. Please use the left hand n
features/configuration
features/routing
features/requestaggregation
+ features/graphql
features/servicediscovery
features/servicefabric
features/authentication
features/authorisation
+ features/websockets
features/administration
features/ratelimiting
features/caching
diff --git a/docs/introduction/bigpicture.rst b/docs/introduction/bigpicture.rst
index 989c0f5c..b047abb4 100644
--- a/docs/introduction/bigpicture.rst
+++ b/docs/introduction/bigpicture.rst
@@ -1,7 +1,7 @@
Big Picture
===========
-Ocleot is aimed at people using .NET running
+Ocelot is aimed at people using .NET running
a micro services / service orientated architecture
that need a unified point of entry into their system.
diff --git a/docs/introduction/gettingstarted.rst b/docs/introduction/gettingstarted.rst
index 8f635fdc..089acc4f 100644
--- a/docs/introduction/gettingstarted.rst
+++ b/docs/introduction/gettingstarted.rst
@@ -9,7 +9,7 @@ built to netcoreapp2.0 `this `_.
**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
@@ -55,7 +55,7 @@ AddOcelot() (adds ocelot services), UseOcelot().Wait() (sets up all the Ocelot m
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
- .AddJsonFile("configuration.json")
+ .AddJsonFile("ocelot.json")
.AddEnvironmentVariables();
})
.ConfigureServices(s => {
@@ -87,7 +87,7 @@ All versions can be found `here `_.
**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
@@ -135,7 +135,7 @@ An example startup using a json file for configuration can be seen below.
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
- .AddJsonFile("configuration.json")
+ .AddJsonFile("ocelot.json")
.AddEnvironmentVariables();
Configuration = builder.Build();
@@ -154,4 +154,4 @@ An example startup using a json file for configuration can be seen below.
}
}
-This is pretty much all you need to get going.
\ No newline at end of file
+This is pretty much all you need to get going.
diff --git a/docs/introduction/notsupported.rst b/docs/introduction/notsupported.rst
index 35c916a0..e578bca2 100644
--- a/docs/introduction/notsupported.rst
+++ b/docs/introduction/notsupported.rst
@@ -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 :(
-* 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
@@ -25,8 +28,16 @@ Ocelot does not support...
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.
\ No newline at end of file
+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.
\ No newline at end of file
diff --git a/samples/OcelotGraphQL/OcelotGraphQL.csproj b/samples/OcelotGraphQL/OcelotGraphQL.csproj
new file mode 100644
index 00000000..ec015d1b
--- /dev/null
+++ b/samples/OcelotGraphQL/OcelotGraphQL.csproj
@@ -0,0 +1,18 @@
+
+
+ netcoreapp2.0
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/OcelotGraphQL/Program.cs b/samples/OcelotGraphQL/Program.cs
new file mode 100644
index 00000000..81938f9c
--- /dev/null
+++ b/samples/OcelotGraphQL/Program.cs
@@ -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 _heroes = new List
+ {
+ 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 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();
+ });
+
+ 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(schema);
+ s.AddOcelot()
+ .AddSingletonDelegatingHandler();
+ })
+ .ConfigureLogging((hostingContext, logging) =>
+ {
+ logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
+ logging.AddConsole();
+ })
+ .UseIISIntegration()
+ .Configure(app =>
+ {
+ app.UseOcelot().Wait();
+ })
+ .Build()
+ .Run();
+ }
+ }
+}
diff --git a/samples/OcelotGraphQL/README.md b/samples/OcelotGraphQL/README.md
new file mode 100644
index 00000000..b2101d56
--- /dev/null
+++ b/samples/OcelotGraphQL/README.md
@@ -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"
+ ]
+ }
+ ]
+ }
+```
\ No newline at end of file
diff --git a/samples/OcelotGraphQL/ocelot.json b/samples/OcelotGraphQL/ocelot.json
new file mode 100644
index 00000000..115006f9
--- /dev/null
+++ b/samples/OcelotGraphQL/ocelot.json
@@ -0,0 +1,19 @@
+{
+ "ReRoutes": [
+ {
+ "DownstreamPathTemplate": "/",
+ "DownstreamScheme": "http",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "jsonplaceholder.typicode.com",
+ "Port": 80
+ }
+ ],
+ "UpstreamPathTemplate": "/graphql",
+ "DelegatingHandlers": [
+ "GraphQlDelegatingHandler"
+ ]
+ }
+ ]
+ }
+
\ No newline at end of file
diff --git a/samples/OcelotServiceFabric/.gitignore b/samples/OcelotServiceFabric/.gitignore
index 733dbb07..84aa4b08 100644
--- a/samples/OcelotServiceFabric/.gitignore
+++ b/samples/OcelotServiceFabric/.gitignore
@@ -13,7 +13,7 @@
# Service fabric
OcelotApplicationApiGatewayPkg/Code
OcelotApplication/OcelotApplicationApiGatewayPkg/Code/appsettings.json
-OcelotApplication/OcelotApplicationApiGatewayPkg/Code/configuration.json
+OcelotApplication/OcelotApplicationApiGatewayPkg/Code/ocelot.json
OcelotApplication/OcelotApplicationApiGatewayPkg/Code/runtimes/
OcelotApplicationServicePkg/Code
OcelotApplication/OcelotApplicationApiGatewayPkg/Code/web.config
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.csproj b/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.csproj
index 1ff69c09..08324cbe 100644
--- a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.csproj
+++ b/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.csproj
@@ -8,7 +8,7 @@
OcelotApplicationApiGateway
-
+
PreserveNewest
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/WebCommunicationListener.cs b/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/WebCommunicationListener.cs
index 6685e11f..7d913cfa 100644
--- a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/WebCommunicationListener.cs
+++ b/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/WebCommunicationListener.cs
@@ -67,7 +67,7 @@ namespace OcelotApplicationApiGateway
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
- .AddJsonFile("configuration.json")
+ .AddJsonFile("ocelot.json")
.AddEnvironmentVariables();
})
.ConfigureLogging((hostingContext, logging) =>
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/configuration.json b/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/ocelot.json
similarity index 100%
rename from samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/configuration.json
rename to samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/ocelot.json
diff --git a/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs b/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs
index bd4683d0..2c70e394 100644
--- a/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs
+++ b/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs
@@ -12,20 +12,19 @@ namespace Ocelot.Authentication.Middleware
public class AuthenticationMiddleware : OcelotMiddleware
{
private readonly OcelotRequestDelegate _next;
- private readonly IOcelotLogger _logger;
public AuthenticationMiddleware(OcelotRequestDelegate next,
IOcelotLoggerFactory loggerFactory)
+ : base(loggerFactory.CreateLogger())
{
_next = next;
- _logger = loggerFactory.CreateLogger();
}
public async Task Invoke(DownstreamContext context)
{
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);
@@ -33,25 +32,22 @@ namespace Ocelot.Authentication.Middleware
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);
}
else
{
- var error = new List
- {
- new UnauthenticatedError(
- $"Request for authenticated route {context.HttpContext.Request.Path} by {context.HttpContext.User.Identity.Name} was unauthenticated")
- };
+ var error = 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);
}
}
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);
}
diff --git a/src/Ocelot/Authorisation/ClaimsAuthoriser.cs b/src/Ocelot/Authorisation/ClaimsAuthoriser.cs
index 3ca7809e..d67c4d5f 100644
--- a/src/Ocelot/Authorisation/ClaimsAuthoriser.cs
+++ b/src/Ocelot/Authorisation/ClaimsAuthoriser.cs
@@ -1,6 +1,5 @@
using System.Collections.Generic;
using System.Security.Claims;
-using Ocelot.Errors;
using Ocelot.Responses;
namespace Ocelot.Authorisation
@@ -32,19 +31,13 @@ namespace Ocelot.Authorisation
var authorised = values.Data.Contains(required.Value);
if (!authorised)
{
- return new ErrorResponse(new List
- {
- new ClaimValueNotAuthorisedError(
- $"claim value: {values.Data} is not the same as required value: {required.Value} for type: {required.Key}")
- });
+ return new ErrorResponse(new ClaimValueNotAuthorisedError(
+ $"claim value: {values.Data} is not the same as required value: {required.Value} for type: {required.Key}"));
}
}
else
{
- return new ErrorResponse(new List
- {
- new UserDoesNotHaveClaimError($"user does not have claim {required.Key}")
- });
+ return new ErrorResponse(new UserDoesNotHaveClaimError($"user does not have claim {required.Key}"));
}
}
diff --git a/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs b/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs
index 4ace7dbc..82fc3af7 100644
--- a/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs
+++ b/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs
@@ -13,30 +13,29 @@
private readonly OcelotRequestDelegate _next;
private readonly IClaimsAuthoriser _claimsAuthoriser;
private readonly IScopesAuthoriser _scopesAuthoriser;
- private readonly IOcelotLogger _logger;
public AuthorisationMiddleware(OcelotRequestDelegate next,
IClaimsAuthoriser claimsAuthoriser,
IScopesAuthoriser scopesAuthoriser,
IOcelotLoggerFactory loggerFactory)
+ :base(loggerFactory.CreateLogger())
{
_next = next;
_claimsAuthoriser = claimsAuthoriser;
_scopesAuthoriser = scopesAuthoriser;
- _logger = loggerFactory.CreateLogger();
}
public async Task Invoke(DownstreamContext context)
{
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);
if (authorised.IsError)
{
- _logger.LogDebug("error authorising user scopes");
+ Logger.LogWarning("error authorising user scopes");
SetPipelineError(context, authorised.Errors);
return;
@@ -44,29 +43,26 @@
if (IsAuthorised(authorised))
{
- _logger.LogDebug("user scopes is authorised calling next authorisation checks");
+ Logger.LogInformation("user scopes is authorised calling next authorisation checks");
}
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
- {
- new UnauthorisedError(
- $"{context.HttpContext.User.Identity.Name} unable to access {context.DownstreamReRoute.UpstreamPathTemplate.Value}")
- });
+ SetPipelineError(context, new UnauthorisedError(
+ $"{context.HttpContext.User.Identity.Name} unable to access {context.DownstreamReRoute.UpstreamPathTemplate.Value}"));
}
}
if (IsAuthorisedRoute(context.DownstreamReRoute))
{
- _logger.LogDebug("route is authorised");
+ Logger.LogInformation("route is authorised");
var authorised = _claimsAuthoriser.Authorise(context.HttpContext.User, context.DownstreamReRoute.RouteClaimsRequirement);
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);
return;
@@ -74,22 +70,19 @@
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);
}
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
- {
- new UnauthorisedError($"{context.HttpContext.User.Identity.Name} is not authorised to access {context.DownstreamReRoute.UpstreamPathTemplate.Value}")
- });
+ SetPipelineError(context, new UnauthorisedError($"{context.HttpContext.User.Identity.Name} is not authorised to access {context.DownstreamReRoute.UpstreamPathTemplate.Value}"));
}
}
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);
}
}
diff --git a/src/Ocelot/Authorisation/ScopesAuthoriser.cs b/src/Ocelot/Authorisation/ScopesAuthoriser.cs
index 1654e2a1..4b999c10 100644
--- a/src/Ocelot/Authorisation/ScopesAuthoriser.cs
+++ b/src/Ocelot/Authorisation/ScopesAuthoriser.cs
@@ -1,5 +1,4 @@
using IdentityModel;
-using Ocelot.Errors;
using Ocelot.Responses;
using System.Collections.Generic;
using System.Security.Claims;
@@ -38,11 +37,8 @@ namespace Ocelot.Authorisation
if (matchesScopes.Count == 0)
{
- return new ErrorResponse(new List
- {
- new ScopeNotAuthorisedError(
- $"no one user scope: '{string.Join(",", userScopes)}' match with some allowed scope: '{string.Join(",", routeAllowedScopes)}'")
- });
+ return new ErrorResponse(
+ new ScopeNotAuthorisedError($"no one user scope: '{string.Join(",", userScopes)}' match with some allowed scope: '{string.Join(",", routeAllowedScopes)}'"));
}
return new OkResponse(true);
diff --git a/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs b/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs
index 3d128b72..aee3bec3 100644
--- a/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs
+++ b/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs
@@ -2,19 +2,16 @@
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
-using Microsoft.AspNetCore.Http;
-using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging;
using Ocelot.Middleware;
using System.IO;
-using Ocelot.DownstreamRouteFinder.Middleware;
+using Ocelot.Middleware.Multiplexer;
namespace Ocelot.Cache.Middleware
{
public class OutputCacheMiddleware : OcelotMiddleware
{
private readonly OcelotRequestDelegate _next;
- private readonly IOcelotLogger _logger;
private readonly IOcelotCache _outputCache;
private readonly IRegionCreator _regionCreator;
@@ -22,10 +19,10 @@ namespace Ocelot.Cache.Middleware
IOcelotLoggerFactory loggerFactory,
IOcelotCache outputCache,
IRegionCreator regionCreator)
+ :base(loggerFactory.CreateLogger())
{
_next = next;
_outputCache = outputCache;
- _logger = loggerFactory.CreateLogger();
_regionCreator = regionCreator;
}
@@ -37,31 +34,31 @@ namespace Ocelot.Cache.Middleware
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);
if (cached != null)
{
- _logger.LogDebug("cache entry exists for {downstreamUrlKey}", downstreamUrlKey);
+ Logger.LogDebug($"cache entry exists for {downstreamUrlKey}");
var response = CreateHttpResponseMessage(cached);
SetHttpResponseMessageThisRequest(context, response);
- _logger.LogDebug("finished returned cached response for {downstreamUrlKey}", downstreamUrlKey);
+ Logger.LogDebug($"finished returned cached response for {downstreamUrlKey}");
return;
}
- _logger.LogDebug("no resonse cached for {downstreamUrlKey}", downstreamUrlKey);
+ Logger.LogDebug($"no resonse cached for {downstreamUrlKey}");
await _next.Invoke(context);
if (context.IsError)
{
- _logger.LogDebug("there was a pipeline error for {downstreamUrlKey}", downstreamUrlKey);
+ Logger.LogDebug($"there was a pipeline error for {downstreamUrlKey}");
return;
}
@@ -70,41 +67,34 @@ namespace Ocelot.Cache.Middleware
_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;
}
- internal HttpResponseMessage CreateHttpResponseMessage(CachedResponse cached)
+ internal DownstreamResponse CreateHttpResponseMessage(CachedResponse cached)
{
if (cached == 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));
- response.Content = new StreamContent(content);
+ var streamContent = new StreamContent(content);
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 CreateCachedResponse(HttpResponseMessage response)
+ internal async Task CreateCachedResponse(DownstreamResponse response)
{
if (response == null)
{
@@ -112,7 +102,7 @@ namespace Ocelot.Cache.Middleware
}
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;
if (response.Content != null)
diff --git a/src/Ocelot/Cache/OutputCacheController.cs b/src/Ocelot/Cache/OutputCacheController.cs
index 32b9fa7c..cc33e8aa 100644
--- a/src/Ocelot/Cache/OutputCacheController.cs
+++ b/src/Ocelot/Cache/OutputCacheController.cs
@@ -1,9 +1,5 @@
-using System.Net.Http;
-using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
-using Ocelot.Cache;
-using Ocelot.Configuration.Provider;
namespace Ocelot.Cache
{
@@ -11,7 +7,7 @@ namespace Ocelot.Cache
[Route("outputcache")]
public class OutputCacheController : Controller
{
- private IOcelotCache _cache;
+ private readonly IOcelotCache _cache;
public OutputCacheController(IOcelotCache cache)
{
@@ -26,4 +22,4 @@ namespace Ocelot.Cache
return new NoContentResult();
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Ocelot/Claims/Middleware/ClaimsBuilderMiddleware.cs b/src/Ocelot/Claims/Middleware/ClaimsBuilderMiddleware.cs
index 48f57834..2dfc3dc0 100644
--- a/src/Ocelot/Claims/Middleware/ClaimsBuilderMiddleware.cs
+++ b/src/Ocelot/Claims/Middleware/ClaimsBuilderMiddleware.cs
@@ -12,28 +12,27 @@ namespace Ocelot.Claims.Middleware
{
private readonly OcelotRequestDelegate _next;
private readonly IAddClaimsToRequest _addClaimsToRequest;
- private readonly IOcelotLogger _logger;
public ClaimsBuilderMiddleware(OcelotRequestDelegate next,
IOcelotLoggerFactory loggerFactory,
IAddClaimsToRequest addClaimsToRequest)
+ :base(loggerFactory.CreateLogger())
{
_next = next;
_addClaimsToRequest = addClaimsToRequest;
- _logger = loggerFactory.CreateLogger();
}
public async Task Invoke(DownstreamContext context)
{
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);
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);
return;
diff --git a/src/Ocelot/Configuration/Authentication/HashMatcher.cs b/src/Ocelot/Configuration/Authentication/HashMatcher.cs
deleted file mode 100644
index 5f17362f..00000000
--- a/src/Ocelot/Configuration/Authentication/HashMatcher.cs
+++ /dev/null
@@ -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;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Ocelot/Configuration/Authentication/IHashMatcher.cs b/src/Ocelot/Configuration/Authentication/IHashMatcher.cs
deleted file mode 100644
index 629bf008..00000000
--- a/src/Ocelot/Configuration/Authentication/IHashMatcher.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Ocelot.Configuration.Authentication
-{
- public interface IHashMatcher
- {
- bool Match(string password, string salt, string hash);
- }
-}
\ No newline at end of file
diff --git a/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs b/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs
index 0e28aaa7..15fddb97 100644
--- a/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs
+++ b/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs
@@ -2,6 +2,7 @@ using System.Collections.Generic;
using System.Net.Http;
using Ocelot.Values;
using System.Linq;
+using Ocelot.Configuration.Creator;
namespace Ocelot.Configuration.Builder
{
@@ -37,11 +38,16 @@ namespace Ocelot.Configuration.Builder
private string _upstreamHost;
private string _key;
private List _delegatingHandlers;
+ private List _addHeadersToDownstream;
+ private List _addHeadersToUpstream;
+ private bool _dangerousAcceptAnyServerCertificateValidator;
public DownstreamReRouteBuilder()
{
_downstreamAddresses = new List();
_delegatingHandlers = new List();
+ _addHeadersToDownstream = new List();
+ _addHeadersToUpstream = new List();
}
public DownstreamReRouteBuilder WithDownstreamAddresses(List downstreamAddresses)
@@ -224,6 +230,24 @@ namespace Ocelot.Configuration.Builder
return this;
}
+ public DownstreamReRouteBuilder WithAddHeadersToDownstream(List addHeadersToDownstream)
+ {
+ _addHeadersToDownstream = addHeadersToDownstream;
+ return this;
+ }
+
+ public DownstreamReRouteBuilder WithAddHeadersToUpstream(List addHeadersToUpstream)
+ {
+ _addHeadersToUpstream = addHeadersToUpstream;
+ return this;
+ }
+
+ public DownstreamReRouteBuilder WithDangerousAcceptAnyServerCertificateValidator(bool dangerousAcceptAnyServerCertificateValidator)
+ {
+ _dangerousAcceptAnyServerCertificateValidator = dangerousAcceptAnyServerCertificateValidator;
+ return this;
+ }
+
public DownstreamReRoute Build()
{
return new DownstreamReRoute(
@@ -253,7 +277,10 @@ namespace Ocelot.Configuration.Builder
_authenticationOptions,
new PathTemplate(_downstreamPathTemplate),
_reRouteKey,
- _delegatingHandlers);
+ _delegatingHandlers,
+ _addHeadersToDownstream,
+ _addHeadersToUpstream,
+ _dangerousAcceptAnyServerCertificateValidator);
}
}
}
diff --git a/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs b/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs
index 0d059932..31367f26 100644
--- a/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs
+++ b/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs
@@ -14,6 +14,7 @@ namespace Ocelot.Configuration.Builder
private List _upstreamHttpMethod;
private string _upstreamHost;
private List _downstreamReRoutes;
+ private string _aggregator;
public ReRouteBuilder()
{
@@ -56,6 +57,12 @@ namespace Ocelot.Configuration.Builder
return this;
}
+ public ReRouteBuilder WithAggregator(string aggregator)
+ {
+ _aggregator = aggregator;
+ return this;
+ }
+
public ReRoute Build()
{
return new ReRoute(
@@ -63,7 +70,8 @@ namespace Ocelot.Configuration.Builder
new PathTemplate(_upstreamTemplate),
_upstreamHttpMethod,
_upstreamTemplatePattern,
- _upstreamHost
+ _upstreamHost,
+ _aggregator
);
}
}
diff --git a/src/Ocelot/Configuration/Builder/ServiceProviderConfigurationBuilder.cs b/src/Ocelot/Configuration/Builder/ServiceProviderConfigurationBuilder.cs
index b1f4e832..cd8446a5 100644
--- a/src/Ocelot/Configuration/Builder/ServiceProviderConfigurationBuilder.cs
+++ b/src/Ocelot/Configuration/Builder/ServiceProviderConfigurationBuilder.cs
@@ -5,28 +5,35 @@ namespace Ocelot.Configuration.Builder
private string _serviceDiscoveryProviderHost;
private int _serviceDiscoveryProviderPort;
private string _type;
+ private string _token;
- public ServiceProviderConfigurationBuilder WithServiceDiscoveryProviderHost(string serviceDiscoveryProviderHost)
+ public ServiceProviderConfigurationBuilder WithHost(string serviceDiscoveryProviderHost)
{
_serviceDiscoveryProviderHost = serviceDiscoveryProviderHost;
return this;
}
- public ServiceProviderConfigurationBuilder WithServiceDiscoveryProviderPort(int serviceDiscoveryProviderPort)
+ public ServiceProviderConfigurationBuilder WithPort(int serviceDiscoveryProviderPort)
{
_serviceDiscoveryProviderPort = serviceDiscoveryProviderPort;
return this;
}
- public ServiceProviderConfigurationBuilder WithServiceDiscoveryProviderType(string type)
+ public ServiceProviderConfigurationBuilder WithType(string type)
{
_type = type;
return this;
}
+ public ServiceProviderConfigurationBuilder WithToken(string token)
+ {
+ _token = token;
+ return this;
+ }
+
public ServiceProviderConfiguration Build()
{
- return new ServiceProviderConfiguration(_type, _serviceDiscoveryProviderHost, _serviceDiscoveryProviderPort);
+ return new ServiceProviderConfiguration(_type, _serviceDiscoveryProviderHost, _serviceDiscoveryProviderPort, _token);
}
}
}
diff --git a/src/Ocelot/Configuration/Creator/AddHeader.cs b/src/Ocelot/Configuration/Creator/AddHeader.cs
new file mode 100644
index 00000000..72cae161
--- /dev/null
+++ b/src/Ocelot/Configuration/Creator/AddHeader.cs
@@ -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; }
+ }
+}
diff --git a/src/Ocelot/Configuration/Creator/ClaimsToThingCreator.cs b/src/Ocelot/Configuration/Creator/ClaimsToThingCreator.cs
index 985abae4..0be61658 100644
--- a/src/Ocelot/Configuration/Creator/ClaimsToThingCreator.cs
+++ b/src/Ocelot/Configuration/Creator/ClaimsToThingCreator.cs
@@ -26,8 +26,7 @@ namespace Ocelot.Configuration.Creator
if (claimToThing.IsError)
{
- _logger.LogDebug("ClaimsToThingCreator.BuildAddThingsToRequest",
- $"Unable to extract configuration for key: {input.Key} and value: {input.Value} your configuration file is incorrect");
+ _logger.LogDebug($"Unable to extract configuration for key: {input.Key} and value: {input.Value} your configuration file is incorrect");
}
else
{
diff --git a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/FileInternalConfigurationCreator.cs
similarity index 88%
rename from src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs
rename to src/Ocelot/Configuration/Creator/FileInternalConfigurationCreator.cs
index 94cde717..46eec9d6 100644
--- a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs
+++ b/src/Ocelot/Configuration/Creator/FileInternalConfigurationCreator.cs
@@ -16,9 +16,8 @@ namespace Ocelot.Configuration.Creator
///
/// Register as singleton
///
- public class FileOcelotConfigurationCreator : IOcelotConfigurationCreator
+ public class FileInternalConfigurationCreator : IInternalConfigurationCreator
{
- private readonly IOptions _options;
private readonly IConfigurationValidator _configurationValidator;
private readonly IOcelotLogger _logger;
private readonly IClaimsToThingCreator _claimsToThingCreator;
@@ -35,8 +34,7 @@ namespace Ocelot.Configuration.Creator
private readonly IHeaderFindAndReplaceCreator _headerFAndRCreator;
private readonly IDownstreamAddressesCreator _downstreamAddressesCreator;
- public FileOcelotConfigurationCreator(
- IOptions options,
+ public FileInternalConfigurationCreator(
IConfigurationValidator configurationValidator,
IOcelotLoggerFactory loggerFactory,
IClaimsToThingCreator claimsToThingCreator,
@@ -62,9 +60,8 @@ namespace Ocelot.Configuration.Creator
_requestIdKeyCreator = requestIdKeyCreator;
_upstreamTemplatePatternCreator = upstreamTemplatePatternCreator;
_authOptionsCreator = authOptionsCreator;
- _options = options;
_configurationValidator = configurationValidator;
- _logger = loggerFactory.CreateLogger();
+ _logger = loggerFactory.CreateLogger();
_claimsToThingCreator = claimsToThingCreator;
_serviceProviderConfigCreator = serviceProviderConfigCreator;
_qosOptionsCreator = qosOptionsCreator;
@@ -72,19 +69,19 @@ namespace Ocelot.Configuration.Creator
_httpHandlerOptionsCreator = httpHandlerOptionsCreator;
}
- public async Task> Create(FileConfiguration fileConfiguration)
+ public async Task> Create(FileConfiguration fileConfiguration)
{
var config = await SetUpConfiguration(fileConfiguration);
return config;
}
- private async Task> SetUpConfiguration(FileConfiguration fileConfiguration)
+ private async Task> SetUpConfiguration(FileConfiguration fileConfiguration)
{
var response = await _configurationValidator.IsValid(fileConfiguration);
if (response.Data.IsError)
{
- return new ErrorResponse(response.Data.Errors);
+ return new ErrorResponse(response.Data.Errors);
}
var reRoutes = new List();
@@ -106,9 +103,9 @@ namespace Ocelot.Configuration.Creator
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(config);
+ return new OkResponse(config);
}
public ReRoute SetUpAggregateReRoute(List reRoutes, FileAggregateReRoute aggregateReRoute, FileGlobalConfiguration globalConfiguration)
@@ -132,6 +129,7 @@ namespace Ocelot.Configuration.Creator
.WithUpstreamTemplatePattern(upstreamTemplatePattern)
.WithDownstreamReRoutes(applicableReRoutes)
.WithUpstreamHost(aggregateReRoute.UpstreamHost)
+ .WithAggregator(aggregateReRoute.Aggregator)
.Build();
return reRoute;
@@ -213,6 +211,9 @@ namespace Ocelot.Configuration.Creator
.WithDownstreamHeaderFindAndReplace(hAndRs.Downstream)
.WithUpstreamHost(fileReRoute.UpstreamHost)
.WithDelegatingHandlers(fileReRoute.DelegatingHandlers)
+ .WithAddHeadersToDownstream(hAndRs.AddHeadersToDownstream)
+ .WithAddHeadersToUpstream(hAndRs.AddHeadersToUpstream)
+ .WithDangerousAcceptAnyServerCertificateValidator(fileReRoute.DangerousAcceptAnyServerCertificateValidator)
.Build();
return reRoute;
diff --git a/src/Ocelot/Configuration/Creator/HeaderFindAndReplaceCreator.cs b/src/Ocelot/Configuration/Creator/HeaderFindAndReplaceCreator.cs
index 7ab33e90..1a5f1b6a 100644
--- a/src/Ocelot/Configuration/Creator/HeaderFindAndReplaceCreator.cs
+++ b/src/Ocelot/Configuration/Creator/HeaderFindAndReplaceCreator.cs
@@ -1,44 +1,76 @@
using System;
using System.Collections.Generic;
using Ocelot.Configuration.File;
+using Ocelot.Infrastructure;
+using Ocelot.Logging;
using Ocelot.Middleware;
+using Ocelot.Responses;
namespace Ocelot.Configuration.Creator
{
public class HeaderFindAndReplaceCreator : IHeaderFindAndReplaceCreator
{
- private IBaseUrlFinder _finder;
- private readonly Dictionary> _placeholders;
+ private readonly IPlaceholders _placeholders;
+ private readonly IOcelotLogger _logger;
- public HeaderFindAndReplaceCreator(IBaseUrlFinder finder)
+ public HeaderFindAndReplaceCreator(IPlaceholders placeholders, IOcelotLoggerFactory factory)
{
- _finder = finder;
- _placeholders = new Dictionary>();
- _placeholders.Add("{BaseUrl}", () => _finder.Find());
+ _logger = factory.CreateLogger();
+ _placeholders = placeholders;
}
public HeaderTransformations Create(FileReRoute fileReRoute)
{
var upstream = new List();
+ var addHeadersToUpstream = new List();
foreach(var input in fileReRoute.UpstreamHeaderTransform)
{
- var hAndr = Map(input);
- upstream.Add(hAndr);
+ if (input.Value.Contains(","))
+ {
+ 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();
-
+ var addHeadersToDownstream = new List();
+
foreach(var input in fileReRoute.DownstreamHeaderTransform)
{
- var hAndr = Map(input);
- downstream.Add(hAndr);
+ if(input.Value.Contains(","))
+ {
+ 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 input)
+ private Response Map(KeyValuePair input)
{
var findAndReplace = input.Value.Split(",");
@@ -51,16 +83,19 @@ namespace Ocelot.Configuration.Creator
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();
- replace = replace.Replace(placeholder, value);
+ return new ErrorResponse(value.Errors);
}
+
+ replace = replace.Replace(placeholder, value.Data);
}
var hAndr = new HeaderFindAndReplace(input.Key, findAndReplace[0], replace, 0);
- return hAndr;
+ return new OkResponse(hAndr);
}
}
}
diff --git a/src/Ocelot/Configuration/Creator/HeaderTransformations.cs b/src/Ocelot/Configuration/Creator/HeaderTransformations.cs
index 55e1e3b9..72b6e1f6 100644
--- a/src/Ocelot/Configuration/Creator/HeaderTransformations.cs
+++ b/src/Ocelot/Configuration/Creator/HeaderTransformations.cs
@@ -4,14 +4,23 @@ namespace Ocelot.Configuration.Creator
{
public class HeaderTransformations
{
- public HeaderTransformations(List upstream, List downstream)
+ public HeaderTransformations(
+ List upstream,
+ List downstream,
+ List addHeaderToDownstream,
+ List addHeaderToUpstream)
{
+ AddHeadersToDownstream = addHeaderToDownstream;
+ AddHeadersToUpstream = addHeaderToUpstream;
Upstream = upstream;
Downstream = downstream;
}
- public List Upstream {get;private set;}
+ public List Upstream { get; }
- public List Downstream {get;private set;}
+ public List Downstream { get; }
+
+ public List AddHeadersToDownstream { get; }
+ public List AddHeadersToUpstream { get; }
}
}
diff --git a/src/Ocelot/Configuration/Creator/HttpHandlerOptionsCreator.cs b/src/Ocelot/Configuration/Creator/HttpHandlerOptionsCreator.cs
index 6c66f3c0..332c25b7 100644
--- a/src/Ocelot/Configuration/Creator/HttpHandlerOptionsCreator.cs
+++ b/src/Ocelot/Configuration/Creator/HttpHandlerOptionsCreator.cs
@@ -1,13 +1,24 @@
-using Ocelot.Configuration.File;
+using Butterfly.Client.Tracing;
+using Ocelot.Configuration.File;
+using Ocelot.Requester;
namespace Ocelot.Configuration.Creator
{
public class HttpHandlerOptionsCreator : IHttpHandlerOptionsCreator
{
+ private IServiceTracer _tracer;
+
+ public HttpHandlerOptionsCreator(IServiceTracer tracer)
+ {
+ _tracer = tracer;
+ }
+
public HttpHandlerOptions Create(FileReRoute fileReRoute)
{
+ var useTracing = _tracer.GetType() != typeof(FakeServiceTracer) ? fileReRoute.HttpHandlerOptions.UseTracing : false;
+
return new HttpHandlerOptions(fileReRoute.HttpHandlerOptions.AllowAutoRedirect,
- fileReRoute.HttpHandlerOptions.UseCookieContainer, fileReRoute.HttpHandlerOptions.UseTracing);
+ fileReRoute.HttpHandlerOptions.UseCookieContainer, useTracing);
}
}
}
diff --git a/src/Ocelot/Configuration/Creator/IInternalConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/IInternalConfigurationCreator.cs
new file mode 100644
index 00000000..a0f3cb42
--- /dev/null
+++ b/src/Ocelot/Configuration/Creator/IInternalConfigurationCreator.cs
@@ -0,0 +1,11 @@
+using System.Threading.Tasks;
+using Ocelot.Configuration.File;
+using Ocelot.Responses;
+
+namespace Ocelot.Configuration.Creator
+{
+ public interface IInternalConfigurationCreator
+ {
+ Task> Create(FileConfiguration fileConfiguration);
+ }
+}
diff --git a/src/Ocelot/Configuration/Creator/IOcelotConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/IOcelotConfigurationCreator.cs
deleted file mode 100644
index 4b431701..00000000
--- a/src/Ocelot/Configuration/Creator/IOcelotConfigurationCreator.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System.Threading.Tasks;
-using Ocelot.Configuration.File;
-using Ocelot.Responses;
-
-namespace Ocelot.Configuration.Creator
-{
- public interface IOcelotConfigurationCreator
- {
- Task> Create(FileConfiguration fileConfiguration);
- }
-}
\ No newline at end of file
diff --git a/src/Ocelot/Configuration/Creator/IdentityServerConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/IdentityServerConfigurationCreator.cs
index 5e7fcd37..8569001e 100644
--- a/src/Ocelot/Configuration/Creator/IdentityServerConfigurationCreator.cs
+++ b/src/Ocelot/Configuration/Creator/IdentityServerConfigurationCreator.cs
@@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
-using IdentityServer4.AccessTokenValidation;
-using IdentityServer4.Models;
-using Ocelot.Configuration.Provider;
namespace Ocelot.Configuration.Creator
{
diff --git a/src/Ocelot/Configuration/Creator/ServiceProviderConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/ServiceProviderConfigurationCreator.cs
index c104385a..aa735f1a 100644
--- a/src/Ocelot/Configuration/Creator/ServiceProviderConfigurationCreator.cs
+++ b/src/Ocelot/Configuration/Creator/ServiceProviderConfigurationCreator.cs
@@ -11,9 +11,10 @@ namespace Ocelot.Configuration.Creator
var serviceProviderPort = globalConfiguration?.ServiceDiscoveryProvider?.Port ?? 0;
return new ServiceProviderConfigurationBuilder()
- .WithServiceDiscoveryProviderHost(globalConfiguration?.ServiceDiscoveryProvider?.Host)
- .WithServiceDiscoveryProviderPort(serviceProviderPort)
- .WithServiceDiscoveryProviderType(globalConfiguration?.ServiceDiscoveryProvider?.Type)
+ .WithHost(globalConfiguration?.ServiceDiscoveryProvider?.Host)
+ .WithPort(serviceProviderPort)
+ .WithType(globalConfiguration?.ServiceDiscoveryProvider?.Type)
+ .WithToken(globalConfiguration?.ServiceDiscoveryProvider?.Token)
.Build();
}
}
diff --git a/src/Ocelot/Configuration/Creator/UpstreamTemplatePatternCreator.cs b/src/Ocelot/Configuration/Creator/UpstreamTemplatePatternCreator.cs
index 833c61db..dbf6a2d1 100644
--- a/src/Ocelot/Configuration/Creator/UpstreamTemplatePatternCreator.cs
+++ b/src/Ocelot/Configuration/Creator/UpstreamTemplatePatternCreator.cs
@@ -42,7 +42,7 @@ namespace Ocelot.Configuration.Creator
if (upstreamTemplate == "/")
{
- return new UpstreamPathTemplate(RegExForwardSlashOnly, 1);
+ return new UpstreamPathTemplate(RegExForwardSlashOnly, reRoute.Priority);
}
if(upstreamTemplate.EndsWith("/"))
@@ -54,7 +54,7 @@ namespace Ocelot.Configuration.Creator
? $"^{upstreamTemplate}{RegExMatchEndString}"
: $"^{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}";
- return new UpstreamPathTemplate(route, 1);
+ return new UpstreamPathTemplate(route, reRoute.Priority);
}
private bool ForwardSlashAndOnePlaceHolder(string upstreamTemplate, List placeholders, int postitionOfPlaceHolderClosingBracket)
diff --git a/src/Ocelot/Configuration/DownstreamReRoute.cs b/src/Ocelot/Configuration/DownstreamReRoute.cs
index 6c9aa1dd..4fe89f22 100644
--- a/src/Ocelot/Configuration/DownstreamReRoute.cs
+++ b/src/Ocelot/Configuration/DownstreamReRoute.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using Ocelot.Configuration.Creator;
using Ocelot.Values;
namespace Ocelot.Configuration
@@ -32,8 +33,13 @@ namespace Ocelot.Configuration
AuthenticationOptions authenticationOptions,
PathTemplate downstreamPathTemplate,
string reRouteKey,
- List delegatingHandlers)
+ List delegatingHandlers,
+ List addHeadersToDownstream,
+ List addHeadersToUpstream,
+ bool dangerousAcceptAnyServerCertificateValidator)
{
+ DangerousAcceptAnyServerCertificateValidator = dangerousAcceptAnyServerCertificateValidator;
+ AddHeadersToDownstream = addHeadersToDownstream;
DelegatingHandlers = delegatingHandlers;
Key = key;
UpstreamPathTemplate = upstreamPathTemplate;
@@ -61,6 +67,7 @@ namespace Ocelot.Configuration
AuthenticationOptions = authenticationOptions;
DownstreamPathTemplate = downstreamPathTemplate;
ReRouteKey = reRouteKey;
+ AddHeadersToUpstream = addHeadersToUpstream;
}
public string Key { get; private set; }
@@ -90,5 +97,8 @@ namespace Ocelot.Configuration
public PathTemplate DownstreamPathTemplate { get; private set; }
public string ReRouteKey { get; private set; }
public List DelegatingHandlers {get;private set;}
+ public List AddHeadersToDownstream {get;private set;}
+ public List AddHeadersToUpstream { get; private set; }
+ public bool DangerousAcceptAnyServerCertificateValidator { get; private set; }
}
}
diff --git a/src/Ocelot/Configuration/File/FileAggregateReRoute.cs b/src/Ocelot/Configuration/File/FileAggregateReRoute.cs
index 8c9eabba..c862094a 100644
--- a/src/Ocelot/Configuration/File/FileAggregateReRoute.cs
+++ b/src/Ocelot/Configuration/File/FileAggregateReRoute.cs
@@ -8,11 +8,14 @@ namespace Ocelot.Configuration.File
public string UpstreamPathTemplate { get;set; }
public string UpstreamHost { get; set; }
public bool ReRouteIsCaseSensitive { get; set; }
+ public string Aggregator { get; set; }
// Only supports GET..are you crazy!! POST, PUT WOULD BE CRAZY!! :)
public List UpstreamHttpMethod
{
get { return new List {"Get"}; }
}
+
+ public int Priority {get;set;} = 1;
}
}
diff --git a/src/Ocelot/Configuration/File/FileHttpHandlerOptions.cs b/src/Ocelot/Configuration/File/FileHttpHandlerOptions.cs
index 7f24b572..2934254c 100644
--- a/src/Ocelot/Configuration/File/FileHttpHandlerOptions.cs
+++ b/src/Ocelot/Configuration/File/FileHttpHandlerOptions.cs
@@ -4,8 +4,8 @@
{
public FileHttpHandlerOptions()
{
- AllowAutoRedirect = true;
- UseCookieContainer = true;
+ AllowAutoRedirect = false;
+ UseCookieContainer = false;
}
public bool AllowAutoRedirect { get; set; }
diff --git a/src/Ocelot/Configuration/File/FileReRoute.cs b/src/Ocelot/Configuration/File/FileReRoute.cs
index adc747f9..acc6572a 100644
--- a/src/Ocelot/Configuration/File/FileReRoute.cs
+++ b/src/Ocelot/Configuration/File/FileReRoute.cs
@@ -20,6 +20,7 @@ namespace Ocelot.Configuration.File
UpstreamHeaderTransform = new Dictionary();
DownstreamHostAndPorts = new List();
DelegatingHandlers = new List();
+ Priority = 1;
}
public string DownstreamPathTemplate { get; set; }
@@ -46,5 +47,8 @@ namespace Ocelot.Configuration.File
public string UpstreamHost { get; set; }
public string Key { get;set; }
public List DelegatingHandlers {get;set;}
+ public int Priority { get;set; }
+ public int Timeout { get; set; }
+ public bool DangerousAcceptAnyServerCertificateValidator { get; set; }
}
}
diff --git a/src/Ocelot/Configuration/File/FileServiceDiscoveryProvider.cs b/src/Ocelot/Configuration/File/FileServiceDiscoveryProvider.cs
index 203cc675..9a96a6d3 100644
--- a/src/Ocelot/Configuration/File/FileServiceDiscoveryProvider.cs
+++ b/src/Ocelot/Configuration/File/FileServiceDiscoveryProvider.cs
@@ -5,5 +5,6 @@ namespace Ocelot.Configuration.File
public string Host {get;set;}
public int Port { get; set; }
public string Type { get; set; }
+ public string Token { get; set; }
}
}
diff --git a/src/Ocelot/Configuration/File/IReRoute.cs b/src/Ocelot/Configuration/File/IReRoute.cs
index 69128d3a..fb7e9313 100644
--- a/src/Ocelot/Configuration/File/IReRoute.cs
+++ b/src/Ocelot/Configuration/File/IReRoute.cs
@@ -4,5 +4,6 @@
{
string UpstreamPathTemplate { get; set; }
bool ReRouteIsCaseSensitive { get; set; }
+ int Priority {get;set;}
}
}
diff --git a/src/Ocelot/Configuration/FileConfigurationController.cs b/src/Ocelot/Configuration/FileConfigurationController.cs
index 7bdf4926..707eb61d 100644
--- a/src/Ocelot/Configuration/FileConfigurationController.cs
+++ b/src/Ocelot/Configuration/FileConfigurationController.cs
@@ -2,34 +2,34 @@ using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.DependencyInjection;
using Ocelot.Configuration.File;
-using Ocelot.Configuration.Provider;
using Ocelot.Configuration.Setter;
using Ocelot.Raft;
using Rafty.Concensus;
namespace Ocelot.Configuration
{
+ using Repository;
+
[Authorize]
[Route("configuration")]
public class FileConfigurationController : Controller
{
- private readonly IFileConfigurationProvider _configGetter;
- private readonly IFileConfigurationSetter _configSetter;
- private readonly IServiceProvider _serviceProvider;
+ private readonly IFileConfigurationRepository _repo;
+ private readonly IFileConfigurationSetter _setter;
+ private readonly IServiceProvider _provider;
- public FileConfigurationController(IFileConfigurationProvider getFileConfig, IFileConfigurationSetter configSetter, IServiceProvider serviceProvider)
+ public FileConfigurationController(IFileConfigurationRepository repo, IFileConfigurationSetter setter, IServiceProvider provider)
{
- _configGetter = getFileConfig;
- _configSetter = configSetter;
- _serviceProvider = serviceProvider;
+ _repo = repo;
+ _setter = setter;
+ _provider = provider;
}
[HttpGet]
public async Task Get()
{
- var response = await _configGetter.Get();
+ var response = await _repo.Get();
if(response.IsError)
{
@@ -43,7 +43,7 @@ namespace Ocelot.Configuration
public async Task Post([FromBody]FileConfiguration fileConfiguration)
{
//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)
{
var node = (INode)test;
@@ -56,7 +56,7 @@ namespace Ocelot.Configuration
return new OkObjectResult(result.Command.Configuration);
}
- var response = await _configSetter.Set(fileConfiguration);
+ var response = await _setter.Set(fileConfiguration);
if (response.IsError)
{
diff --git a/src/Ocelot/Configuration/Provider/IIdentityServerConfiguration.cs b/src/Ocelot/Configuration/IIdentityServerConfiguration.cs
similarity index 67%
rename from src/Ocelot/Configuration/Provider/IIdentityServerConfiguration.cs
rename to src/Ocelot/Configuration/IIdentityServerConfiguration.cs
index 8a76eb9f..0eb70347 100644
--- a/src/Ocelot/Configuration/Provider/IIdentityServerConfiguration.cs
+++ b/src/Ocelot/Configuration/IIdentityServerConfiguration.cs
@@ -1,16 +1,14 @@
-using System.Collections.Generic;
-using IdentityServer4.AccessTokenValidation;
-using IdentityServer4.Models;
-
-namespace Ocelot.Configuration.Provider
-{
- public interface IIdentityServerConfiguration
- {
- string ApiName { get; }
- string ApiSecret { get; }
- bool RequireHttps { get; }
- List AllowedScopes { get; }
- string CredentialsSigningCertificateLocation { get; }
- string CredentialsSigningCertificatePassword { get; }
- }
-}
\ No newline at end of file
+namespace Ocelot.Configuration
+{
+ using System.Collections.Generic;
+
+ public interface IIdentityServerConfiguration
+ {
+ string ApiName { get; }
+ string ApiSecret { get; }
+ bool RequireHttps { get; }
+ List AllowedScopes { get; }
+ string CredentialsSigningCertificateLocation { get; }
+ string CredentialsSigningCertificatePassword { get; }
+ }
+}
diff --git a/src/Ocelot/Configuration/IOcelotConfiguration.cs b/src/Ocelot/Configuration/IInternalConfiguration.cs
similarity index 83%
rename from src/Ocelot/Configuration/IOcelotConfiguration.cs
rename to src/Ocelot/Configuration/IInternalConfiguration.cs
index fd808ae4..c1781c48 100644
--- a/src/Ocelot/Configuration/IOcelotConfiguration.cs
+++ b/src/Ocelot/Configuration/IInternalConfiguration.cs
@@ -1,12 +1,12 @@
-using System.Collections.Generic;
-
-namespace Ocelot.Configuration
-{
- public interface IOcelotConfiguration
- {
- List ReRoutes { get; }
- string AdministrationPath {get;}
- ServiceProviderConfiguration ServiceProviderConfiguration {get;}
- string RequestId {get;}
- }
-}
\ No newline at end of file
+using System.Collections.Generic;
+
+namespace Ocelot.Configuration
+{
+ public interface IInternalConfiguration
+ {
+ List ReRoutes { get; }
+ string AdministrationPath {get;}
+ ServiceProviderConfiguration ServiceProviderConfiguration {get;}
+ string RequestId {get;}
+ }
+}
diff --git a/src/Ocelot/Configuration/Provider/IdentityServerConfiguration.cs b/src/Ocelot/Configuration/IdentityServerConfiguration.cs
similarity index 66%
rename from src/Ocelot/Configuration/Provider/IdentityServerConfiguration.cs
rename to src/Ocelot/Configuration/IdentityServerConfiguration.cs
index 795e6994..b8b00ea2 100644
--- a/src/Ocelot/Configuration/Provider/IdentityServerConfiguration.cs
+++ b/src/Ocelot/Configuration/IdentityServerConfiguration.cs
@@ -1,32 +1,30 @@
-using System.Collections.Generic;
-using IdentityServer4.AccessTokenValidation;
-using IdentityServer4.Models;
-
-namespace Ocelot.Configuration.Provider
-{
- public class IdentityServerConfiguration : IIdentityServerConfiguration
- {
- public IdentityServerConfiguration(
- string apiName,
- bool requireHttps,
- string apiSecret,
- List allowedScopes,
- string credentialsSigningCertificateLocation,
- string credentialsSigningCertificatePassword)
- {
- ApiName = apiName;
- RequireHttps = requireHttps;
- ApiSecret = apiSecret;
- AllowedScopes = allowedScopes;
- CredentialsSigningCertificateLocation = credentialsSigningCertificateLocation;
- CredentialsSigningCertificatePassword = credentialsSigningCertificatePassword;
- }
-
- public string ApiName { get; private set; }
- public bool RequireHttps { get; private set; }
- public List AllowedScopes { get; private set; }
- public string ApiSecret { get; private set; }
- public string CredentialsSigningCertificateLocation { get; private set; }
- public string CredentialsSigningCertificatePassword { get; private set; }
- }
-}
\ No newline at end of file
+namespace Ocelot.Configuration
+{
+ using System.Collections.Generic;
+
+ public class IdentityServerConfiguration : IIdentityServerConfiguration
+ {
+ public IdentityServerConfiguration(
+ string apiName,
+ bool requireHttps,
+ string apiSecret,
+ List allowedScopes,
+ string credentialsSigningCertificateLocation,
+ string credentialsSigningCertificatePassword)
+ {
+ ApiName = apiName;
+ RequireHttps = requireHttps;
+ ApiSecret = apiSecret;
+ AllowedScopes = allowedScopes;
+ CredentialsSigningCertificateLocation = credentialsSigningCertificateLocation;
+ CredentialsSigningCertificatePassword = credentialsSigningCertificatePassword;
+ }
+
+ public string ApiName { get; }
+ public bool RequireHttps { get; }
+ public List AllowedScopes { get; }
+ public string ApiSecret { get; }
+ public string CredentialsSigningCertificateLocation { get; }
+ public string CredentialsSigningCertificatePassword { get; }
+ }
+}
diff --git a/src/Ocelot/Configuration/OcelotConfiguration.cs b/src/Ocelot/Configuration/InternalConfiguration.cs
similarity index 67%
rename from src/Ocelot/Configuration/OcelotConfiguration.cs
rename to src/Ocelot/Configuration/InternalConfiguration.cs
index 1ab73b87..429bb9c0 100644
--- a/src/Ocelot/Configuration/OcelotConfiguration.cs
+++ b/src/Ocelot/Configuration/InternalConfiguration.cs
@@ -1,20 +1,20 @@
-using System.Collections.Generic;
-
-namespace Ocelot.Configuration
-{
- public class OcelotConfiguration : IOcelotConfiguration
- {
- public OcelotConfiguration(List reRoutes, string administrationPath, ServiceProviderConfiguration serviceProviderConfiguration, string requestId)
- {
- ReRoutes = reRoutes;
- AdministrationPath = administrationPath;
- ServiceProviderConfiguration = serviceProviderConfiguration;
- RequestId = requestId;
- }
-
- public List ReRoutes { get; }
- public string AdministrationPath {get;}
- public ServiceProviderConfiguration ServiceProviderConfiguration {get;}
- public string RequestId {get;}
- }
-}
+using System.Collections.Generic;
+
+namespace Ocelot.Configuration
+{
+ public class InternalConfiguration : IInternalConfiguration
+ {
+ public InternalConfiguration(List reRoutes, string administrationPath, ServiceProviderConfiguration serviceProviderConfiguration, string requestId)
+ {
+ ReRoutes = reRoutes;
+ AdministrationPath = administrationPath;
+ ServiceProviderConfiguration = serviceProviderConfiguration;
+ RequestId = requestId;
+ }
+
+ public List ReRoutes { get; }
+ public string AdministrationPath {get;}
+ public ServiceProviderConfiguration ServiceProviderConfiguration {get;}
+ public string RequestId {get;}
+ }
+}
diff --git a/src/Ocelot/Configuration/Parser/ClaimToThingConfigurationParser.cs b/src/Ocelot/Configuration/Parser/ClaimToThingConfigurationParser.cs
index 4d698e96..94df168f 100644
--- a/src/Ocelot/Configuration/Parser/ClaimToThingConfigurationParser.cs
+++ b/src/Ocelot/Configuration/Parser/ClaimToThingConfigurationParser.cs
@@ -20,22 +20,14 @@ namespace Ocelot.Configuration.Parser
if (instructions.Length <= 1)
{
- return new ErrorResponse(
- new List
- {
- new NoInstructionsError(SplitToken)
- });
+ return new ErrorResponse(new NoInstructionsError(SplitToken));
}
var claimMatch = _claimRegex.IsMatch(instructions[0]);
if (!claimMatch)
{
- return new ErrorResponse(
- new List
- {
- new InstructionNotForClaimsError()
- });
+ return new ErrorResponse(new InstructionNotForClaimsError());
}
var newKey = GetIndexValue(instructions[0]);
@@ -53,11 +45,7 @@ namespace Ocelot.Configuration.Parser
}
catch (Exception exception)
{
- return new ErrorResponse(
- new List
- {
- new ParsingConfigurationHeaderError(exception)
- });
+ return new ErrorResponse(new ParsingConfigurationHeaderError(exception));
}
}
diff --git a/src/Ocelot/Configuration/Provider/FileConfigurationProvider.cs b/src/Ocelot/Configuration/Provider/FileConfigurationProvider.cs
deleted file mode 100644
index fdcd949b..00000000
--- a/src/Ocelot/Configuration/Provider/FileConfigurationProvider.cs
+++ /dev/null
@@ -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> Get()
- {
- var fileConfig = await _repo.Get();
- return new OkResponse(fileConfig.Data);
- }
- }
-}
\ No newline at end of file
diff --git a/src/Ocelot/Configuration/Provider/IFileConfigurationProvider.cs b/src/Ocelot/Configuration/Provider/IFileConfigurationProvider.cs
deleted file mode 100644
index c2ab51cb..00000000
--- a/src/Ocelot/Configuration/Provider/IFileConfigurationProvider.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System.Threading.Tasks;
-using Ocelot.Configuration.File;
-using Ocelot.Responses;
-
-namespace Ocelot.Configuration.Provider
-{
- public interface IFileConfigurationProvider
- {
- Task> Get();
- }
-}
\ No newline at end of file
diff --git a/src/Ocelot/Configuration/Provider/IOcelotConfigurationProvider.cs b/src/Ocelot/Configuration/Provider/IOcelotConfigurationProvider.cs
deleted file mode 100644
index 80f4583b..00000000
--- a/src/Ocelot/Configuration/Provider/IOcelotConfigurationProvider.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System.Threading.Tasks;
-using Ocelot.Configuration.File;
-using Ocelot.Responses;
-
-namespace Ocelot.Configuration.Provider
-{
- public interface IOcelotConfigurationProvider
- {
- Task> Get();
- }
-}
diff --git a/src/Ocelot/Configuration/Provider/OcelotConfigurationProvider.cs b/src/Ocelot/Configuration/Provider/OcelotConfigurationProvider.cs
deleted file mode 100644
index ed72a60e..00000000
--- a/src/Ocelot/Configuration/Provider/OcelotConfigurationProvider.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using System.Threading.Tasks;
-using Ocelot.Configuration.File;
-using Ocelot.Configuration.Repository;
-using Ocelot.Responses;
-
-namespace Ocelot.Configuration.Provider
-{
- ///
- /// Register as singleton
- ///
- public class OcelotConfigurationProvider : IOcelotConfigurationProvider
- {
- private readonly IOcelotConfigurationRepository _config;
-
- public OcelotConfigurationProvider(IOcelotConfigurationRepository repo)
- {
- _config = repo;
- }
-
- public async Task> Get()
- {
- var repoConfig = await _config.Get();
-
- if (repoConfig.IsError)
- {
- return new ErrorResponse(repoConfig.Errors);
- }
-
- return new OkResponse(repoConfig.Data);
- }
- }
-}
\ No newline at end of file
diff --git a/src/Ocelot/Configuration/QoSOptions.cs b/src/Ocelot/Configuration/QoSOptions.cs
index 651bd506..8c5d6d27 100644
--- a/src/Ocelot/Configuration/QoSOptions.cs
+++ b/src/Ocelot/Configuration/QoSOptions.cs
@@ -16,12 +16,12 @@ namespace Ocelot.Configuration
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; }
}
}
diff --git a/src/Ocelot/Configuration/ReRoute.cs b/src/Ocelot/Configuration/ReRoute.cs
index 47f26291..c4c3952d 100644
--- a/src/Ocelot/Configuration/ReRoute.cs
+++ b/src/Ocelot/Configuration/ReRoute.cs
@@ -12,13 +12,15 @@ namespace Ocelot.Configuration
PathTemplate upstreamPathTemplate,
List upstreamHttpMethod,
UpstreamPathTemplate upstreamTemplatePattern,
- string upstreamHost)
+ string upstreamHost,
+ string aggregator)
{
UpstreamHost = upstreamHost;
DownstreamReRoute = downstreamReRoute;
UpstreamPathTemplate = upstreamPathTemplate;
UpstreamHttpMethod = upstreamHttpMethod;
UpstreamTemplatePattern = upstreamTemplatePattern;
+ Aggregator = aggregator;
}
public PathTemplate UpstreamPathTemplate { get; private set; }
@@ -26,5 +28,6 @@ namespace Ocelot.Configuration
public List UpstreamHttpMethod { get; private set; }
public string UpstreamHost { get; private set; }
public List DownstreamReRoute { get; private set; }
+ public string Aggregator {get; private set;}
}
}
diff --git a/src/Ocelot/Configuration/Repository/ConsulFileConfigurationPoller.cs b/src/Ocelot/Configuration/Repository/ConsulFileConfigurationPoller.cs
index a2780062..16108d15 100644
--- a/src/Ocelot/Configuration/Repository/ConsulFileConfigurationPoller.cs
+++ b/src/Ocelot/Configuration/Repository/ConsulFileConfigurationPoller.cs
@@ -17,10 +17,16 @@ namespace Ocelot.Configuration.Repository
private string _previousAsJson;
private readonly Timer _timer;
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;
+ _config = config;
_logger = factory.CreateLogger();
_repo = repo;
_previousAsJson = "";
@@ -30,22 +36,22 @@ namespace Ocelot.Configuration.Repository
{
return;
}
-
+
_polling = true;
await Poll();
_polling = false;
- }, null, 0, 1000);
+ }, null, 0, _config.Delay);
}
private async Task Poll()
{
- _logger.LogDebug("Started polling consul");
+ _logger.LogInformation("Started polling consul");
var fileConfig = await _repo.Get();
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;
}
@@ -57,14 +63,13 @@ namespace Ocelot.Configuration.Repository
_previousAsJson = asJson;
}
- _logger.LogDebug("Finished polling consul");
+ _logger.LogInformation("Finished polling consul");
}
///
/// We could do object comparison here but performance isnt really a problem. This might be an issue one day!
///
- ///
- ///
+ /// hash of the config
private string ToJson(FileConfiguration config)
{
var currentHash = JsonConvert.SerializeObject(config);
diff --git a/src/Ocelot/Configuration/Repository/ConsulFileConfigurationRepository.cs b/src/Ocelot/Configuration/Repository/ConsulFileConfigurationRepository.cs
index 97bf7c97..165e035b 100644
--- a/src/Ocelot/Configuration/Repository/ConsulFileConfigurationRepository.cs
+++ b/src/Ocelot/Configuration/Repository/ConsulFileConfigurationRepository.cs
@@ -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
{
+ 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
{
- private readonly ConsulClient _consul;
- private string _ocelotConfiguration = "OcelotConfiguration";
+ private readonly IConsulClient _consul;
+ private const string OcelotConfiguration = "InternalConfiguration";
private readonly Cache.IOcelotCache _cache;
+ private readonly IOcelotLogger _logger;
- public ConsulFileConfigurationRepository(Cache.IOcelotCache cache, ServiceProviderConfiguration serviceProviderConfig)
+ public ConsulFileConfigurationRepository(
+ Cache.IOcelotCache cache,
+ IInternalConfigurationRepository repo,
+ IConsulClientFactory factory,
+ IOcelotLoggerFactory loggerFactory)
{
- var consulHost = string.IsNullOrEmpty(serviceProviderConfig?.Host) ? "localhost" : serviceProviderConfig?.Host;
- var consulPort = serviceProviderConfig?.Port ?? 8500;
- var configuration = new ConsulRegistryConfiguration(consulHost, consulPort, _ocelotConfiguration);
+ _logger = loggerFactory.CreateLogger();
_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> Get()
{
- var config = _cache.Get(_ocelotConfiguration, _ocelotConfiguration);
+ var config = _cache.Get(OcelotConfiguration, OcelotConfiguration);
if (config != null)
{
return new OkResponse(config);
}
- var queryResult = await _consul.KV.Get(_ocelotConfiguration);
+ var queryResult = await _consul.KV.Get(OcelotConfiguration);
if (queryResult.Response == null)
{
@@ -54,11 +72,11 @@ namespace Ocelot.Configuration.Repository
public async Task Set(FileConfiguration ocelotConfiguration)
{
- var json = JsonConvert.SerializeObject(ocelotConfiguration);
+ var json = JsonConvert.SerializeObject(ocelotConfiguration, Formatting.Indented);
var bytes = Encoding.UTF8.GetBytes(json);
- var kvPair = new KVPair(_ocelotConfiguration)
+ var kvPair = new KVPair(OcelotConfiguration)
{
Value = bytes
};
@@ -67,7 +85,7 @@ namespace Ocelot.Configuration.Repository
if (result.Response)
{
- _cache.AddAndDelete(_ocelotConfiguration, ocelotConfiguration, TimeSpan.FromSeconds(3), _ocelotConfiguration);
+ _cache.AddAndDelete(OcelotConfiguration, ocelotConfiguration, TimeSpan.FromSeconds(3), OcelotConfiguration);
return new OkResponse();
}
diff --git a/src/Ocelot/Configuration/Repository/FileConfigurationRepository.cs b/src/Ocelot/Configuration/Repository/DiskFileConfigurationRepository.cs
similarity index 73%
rename from src/Ocelot/Configuration/Repository/FileConfigurationRepository.cs
rename to src/Ocelot/Configuration/Repository/DiskFileConfigurationRepository.cs
index ff5e3876..a870419c 100644
--- a/src/Ocelot/Configuration/Repository/FileConfigurationRepository.cs
+++ b/src/Ocelot/Configuration/Repository/DiskFileConfigurationRepository.cs
@@ -1,52 +1,54 @@
-using System;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Hosting;
-using Newtonsoft.Json;
-using Ocelot.Configuration.File;
-using Ocelot.Responses;
-
-namespace Ocelot.Configuration.Repository
-{
- public class FileConfigurationRepository : IFileConfigurationRepository
- {
- private readonly string _configFilePath;
-
- private static readonly object _lock = new object();
-
- public FileConfigurationRepository(IHostingEnvironment hostingEnvironment)
- {
- _configFilePath = $"{AppContext.BaseDirectory}/configuration{(string.IsNullOrEmpty(hostingEnvironment.EnvironmentName) ? string.Empty : ".")}{hostingEnvironment.EnvironmentName}.json";
- }
-
- public Task> Get()
- {
- string jsonConfiguration;
-
- lock(_lock)
- {
- jsonConfiguration = System.IO.File.ReadAllText(_configFilePath);
- }
-
- var fileConfiguration = JsonConvert.DeserializeObject(jsonConfiguration);
-
- return Task.FromResult>(new OkResponse(fileConfiguration));
- }
-
- public Task Set(FileConfiguration fileConfiguration)
- {
- string jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
-
- lock(_lock)
- {
- if (System.IO.File.Exists(_configFilePath))
- {
- System.IO.File.Delete(_configFilePath);
- }
-
- System.IO.File.WriteAllText(_configFilePath, jsonConfiguration);
- }
-
- return Task.FromResult(new OkResponse());
- }
- }
-}
+using System;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Hosting;
+using Newtonsoft.Json;
+using Ocelot.Configuration.File;
+using Ocelot.Responses;
+
+namespace Ocelot.Configuration.Repository
+{
+ public class DiskFileConfigurationRepository : IFileConfigurationRepository
+ {
+ private readonly string _configFilePath;
+
+ private static readonly object _lock = new object();
+
+ private const string ConfigurationFileName = "ocelot";
+
+ public DiskFileConfigurationRepository(IHostingEnvironment hostingEnvironment)
+ {
+ _configFilePath = $"{AppContext.BaseDirectory}/{ConfigurationFileName}{(string.IsNullOrEmpty(hostingEnvironment.EnvironmentName) ? string.Empty : ".")}{hostingEnvironment.EnvironmentName}.json";
+ }
+
+ public Task> Get()
+ {
+ string jsonConfiguration;
+
+ lock(_lock)
+ {
+ jsonConfiguration = System.IO.File.ReadAllText(_configFilePath);
+ }
+
+ var fileConfiguration = JsonConvert.DeserializeObject(jsonConfiguration);
+
+ return Task.FromResult>(new OkResponse(fileConfiguration));
+ }
+
+ public Task Set(FileConfiguration fileConfiguration)
+ {
+ string jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration, Formatting.Indented);
+
+ lock(_lock)
+ {
+ if (System.IO.File.Exists(_configFilePath))
+ {
+ System.IO.File.Delete(_configFilePath);
+ }
+
+ System.IO.File.WriteAllText(_configFilePath, jsonConfiguration);
+ }
+
+ return Task.FromResult(new OkResponse());
+ }
+ }
+}
diff --git a/src/Ocelot/Configuration/Repository/IConsulPollerConfiguration.cs b/src/Ocelot/Configuration/Repository/IConsulPollerConfiguration.cs
new file mode 100644
index 00000000..d1f1430d
--- /dev/null
+++ b/src/Ocelot/Configuration/Repository/IConsulPollerConfiguration.cs
@@ -0,0 +1,7 @@
+namespace Ocelot.Configuration.Repository
+{
+ public interface IConsulPollerConfiguration
+ {
+ int Delay { get; }
+ }
+}
diff --git a/src/Ocelot/Configuration/Repository/IInternalConfigurationRepository.cs b/src/Ocelot/Configuration/Repository/IInternalConfigurationRepository.cs
new file mode 100644
index 00000000..5db4adb5
--- /dev/null
+++ b/src/Ocelot/Configuration/Repository/IInternalConfigurationRepository.cs
@@ -0,0 +1,10 @@
+using Ocelot.Responses;
+
+namespace Ocelot.Configuration.Repository
+{
+ public interface IInternalConfigurationRepository
+ {
+ Response Get();
+ Response AddOrReplace(IInternalConfiguration internalConfiguration);
+ }
+}
diff --git a/src/Ocelot/Configuration/Repository/IOcelotConfigurationRepository.cs b/src/Ocelot/Configuration/Repository/IOcelotConfigurationRepository.cs
deleted file mode 100644
index 16b386a1..00000000
--- a/src/Ocelot/Configuration/Repository/IOcelotConfigurationRepository.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System.Threading.Tasks;
-using Ocelot.Configuration.File;
-using Ocelot.Responses;
-
-namespace Ocelot.Configuration.Repository
-{
- public interface IOcelotConfigurationRepository
- {
- Task> Get();
- Task AddOrReplace(IOcelotConfiguration ocelotConfiguration);
- }
-}
\ No newline at end of file
diff --git a/src/Ocelot/Configuration/Repository/InMemoryConsulPollerConfiguration.cs b/src/Ocelot/Configuration/Repository/InMemoryConsulPollerConfiguration.cs
new file mode 100644
index 00000000..9e411f76
--- /dev/null
+++ b/src/Ocelot/Configuration/Repository/InMemoryConsulPollerConfiguration.cs
@@ -0,0 +1,7 @@
+namespace Ocelot.Configuration.Repository
+{
+ public class InMemoryConsulPollerConfiguration : IConsulPollerConfiguration
+ {
+ public int Delay => 1000;
+ }
+}
diff --git a/src/Ocelot/Configuration/Repository/InMemoryInternalConfigurationRepository.cs b/src/Ocelot/Configuration/Repository/InMemoryInternalConfigurationRepository.cs
new file mode 100644
index 00000000..2ac954e3
--- /dev/null
+++ b/src/Ocelot/Configuration/Repository/InMemoryInternalConfigurationRepository.cs
@@ -0,0 +1,29 @@
+using Ocelot.Responses;
+
+namespace Ocelot.Configuration.Repository
+{
+ ///
+ /// Register as singleton
+ ///
+ public class InMemoryInternalConfigurationRepository : IInternalConfigurationRepository
+ {
+ private static readonly object LockObject = new object();
+
+ private IInternalConfiguration _internalConfiguration;
+
+ public Response Get()
+ {
+ return new OkResponse(_internalConfiguration);
+ }
+
+ public Response AddOrReplace(IInternalConfiguration internalConfiguration)
+ {
+ lock (LockObject)
+ {
+ _internalConfiguration = internalConfiguration;
+ }
+
+ return new OkResponse();
+ }
+ }
+}
diff --git a/src/Ocelot/Configuration/Repository/InMemoryOcelotConfigurationRepository.cs b/src/Ocelot/Configuration/Repository/InMemoryOcelotConfigurationRepository.cs
deleted file mode 100644
index 9ce50ba6..00000000
--- a/src/Ocelot/Configuration/Repository/InMemoryOcelotConfigurationRepository.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using System.Threading.Tasks;
-using Ocelot.Responses;
-
-namespace Ocelot.Configuration.Repository
-{
- ///
- /// Register as singleton
- ///
- public class InMemoryOcelotConfigurationRepository : IOcelotConfigurationRepository
- {
- private static readonly object LockObject = new object();
-
- private IOcelotConfiguration _ocelotConfiguration;
-
- public Task> Get()
- {
- return Task.FromResult>(new OkResponse(_ocelotConfiguration));
- }
-
- public Task AddOrReplace(IOcelotConfiguration ocelotConfiguration)
- {
- lock (LockObject)
- {
- _ocelotConfiguration = ocelotConfiguration;
- }
-
- return Task.FromResult(new OkResponse());
- }
- }
-}
diff --git a/src/Ocelot/Configuration/ServiceProviderConfiguration.cs b/src/Ocelot/Configuration/ServiceProviderConfiguration.cs
index aa7c492c..129d24db 100644
--- a/src/Ocelot/Configuration/ServiceProviderConfiguration.cs
+++ b/src/Ocelot/Configuration/ServiceProviderConfiguration.cs
@@ -2,15 +2,17 @@
{
public class ServiceProviderConfiguration
{
- public ServiceProviderConfiguration(string type, string host, int port)
+ public ServiceProviderConfiguration(string type, string host, int port, string token)
{
Host = host;
Port = port;
+ Token = token;
Type = type;
}
- public string Host { get; private set; }
- public int Port { get; private set; }
- public string Type { get; private set; }
+ public string Host { get; }
+ public int Port { get; }
+ public string Type { get; }
+ public string Token { get; }
}
-}
\ No newline at end of file
+}
diff --git a/src/Ocelot/Configuration/Setter/FileConfigurationSetter.cs b/src/Ocelot/Configuration/Setter/FileAndInternalConfigurationSetter.cs
similarity index 63%
rename from src/Ocelot/Configuration/Setter/FileConfigurationSetter.cs
rename to src/Ocelot/Configuration/Setter/FileAndInternalConfigurationSetter.cs
index 38a7c1cb..6821553e 100644
--- a/src/Ocelot/Configuration/Setter/FileConfigurationSetter.cs
+++ b/src/Ocelot/Configuration/Setter/FileAndInternalConfigurationSetter.cs
@@ -1,42 +1,44 @@
-using System.Threading.Tasks;
-using Ocelot.Configuration.Creator;
-using Ocelot.Configuration.File;
-using Ocelot.Configuration.Repository;
-using Ocelot.Responses;
-
-namespace Ocelot.Configuration.Setter
-{
- public class FileConfigurationSetter : IFileConfigurationSetter
- {
- private readonly IOcelotConfigurationRepository _configRepo;
- private readonly IOcelotConfigurationCreator _configCreator;
- private readonly IFileConfigurationRepository _repo;
-
- public FileConfigurationSetter(IOcelotConfigurationRepository configRepo,
- IOcelotConfigurationCreator configCreator, IFileConfigurationRepository repo)
- {
- _configRepo = configRepo;
- _configCreator = configCreator;
- _repo = repo;
- }
-
- public async Task Set(FileConfiguration fileConfig)
- {
- var response = await _repo.Set(fileConfig);
-
- if(response.IsError)
- {
- return new ErrorResponse(response.Errors);
- }
-
- var config = await _configCreator.Create(fileConfig);
-
- if(!config.IsError)
- {
- await _configRepo.AddOrReplace(config.Data);
- }
-
- return new ErrorResponse(config.Errors);
- }
- }
-}
+using System.Threading.Tasks;
+using Ocelot.Configuration.Creator;
+using Ocelot.Configuration.File;
+using Ocelot.Configuration.Repository;
+using Ocelot.Responses;
+
+namespace Ocelot.Configuration.Setter
+{
+ public class FileAndInternalConfigurationSetter : IFileConfigurationSetter
+ {
+ private readonly IInternalConfigurationRepository _configRepo;
+ private readonly IInternalConfigurationCreator _configCreator;
+ private readonly IFileConfigurationRepository _repo;
+
+ public FileAndInternalConfigurationSetter(
+ IInternalConfigurationRepository configRepo,
+ IInternalConfigurationCreator configCreator,
+ IFileConfigurationRepository repo)
+ {
+ _configRepo = configRepo;
+ _configCreator = configCreator;
+ _repo = repo;
+ }
+
+ public async Task Set(FileConfiguration fileConfig)
+ {
+ var response = await _repo.Set(fileConfig);
+
+ if(response.IsError)
+ {
+ return new ErrorResponse(response.Errors);
+ }
+
+ var config = await _configCreator.Create(fileConfig);
+
+ if(!config.IsError)
+ {
+ _configRepo.AddOrReplace(config.Data);
+ }
+
+ return new ErrorResponse(config.Errors);
+ }
+ }
+}
diff --git a/src/Ocelot/Configuration/Validator/ConfigurationValidationResult.cs b/src/Ocelot/Configuration/Validator/ConfigurationValidationResult.cs
index 4fe3567d..7e06cb6b 100644
--- a/src/Ocelot/Configuration/Validator/ConfigurationValidationResult.cs
+++ b/src/Ocelot/Configuration/Validator/ConfigurationValidationResult.cs
@@ -17,8 +17,8 @@ namespace Ocelot.Configuration.Validator
Errors = errors;
}
- public bool IsError { get; private set; }
+ public bool IsError { get; }
- public List Errors { get; private set; }
+ public List Errors { get; }
}
}
diff --git a/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs b/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs
index 56360ad5..9e471475 100644
--- a/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs
+++ b/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs
@@ -1,21 +1,73 @@
-using System;
-using System.Collections.Generic;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.Configuration.Memory;
-
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
{
- [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)
{
- var memorySource = new MemoryConfigurationSource();
- memorySource.InitialData = new List>
+ var memorySource = new MemoryConfigurationSource
{
- new KeyValuePair("BaseUrl", baseUrl)
+ InitialData = new List>
+ {
+ new KeyValuePair("BaseUrl", baseUrl)
+ }
};
+
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(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;
}
}
diff --git a/src/Ocelot/DependencyInjection/IOcelotBuilder.cs b/src/Ocelot/DependencyInjection/IOcelotBuilder.cs
index 37cfa0d8..7cf514cb 100644
--- a/src/Ocelot/DependencyInjection/IOcelotBuilder.cs
+++ b/src/Ocelot/DependencyInjection/IOcelotBuilder.cs
@@ -3,6 +3,7 @@ using CacheManager.Core;
using System;
using System.Net.Http;
using IdentityServer4.AccessTokenValidation;
+using Ocelot.Middleware.Multiplexer;
namespace Ocelot.DependencyInjection
{
@@ -20,8 +21,13 @@ namespace Ocelot.DependencyInjection
IOcelotBuilder AddSingletonDelegatingHandler(bool global = false)
where T : DelegatingHandler;
-
+
IOcelotBuilder AddTransientDelegatingHandler(bool global = false)
where T : DelegatingHandler;
+
+ IOcelotBuilder AddSingletonDefinedAggregator()
+ where T : class, IDefinedAggregator;
+ IOcelotBuilder AddTransientDefinedAggregator()
+ where T : class, IDefinedAggregator;
}
}
diff --git a/src/Ocelot/DependencyInjection/OcelotBuilder.cs b/src/Ocelot/DependencyInjection/OcelotBuilder.cs
index 9e2e763b..672e8540 100644
--- a/src/Ocelot/DependencyInjection/OcelotBuilder.cs
+++ b/src/Ocelot/DependencyInjection/OcelotBuilder.cs
@@ -1,7 +1,3 @@
-using Butterfly.Client.Tracing;
-using Microsoft.Extensions.Options;
-using Ocelot.Middleware.Multiplexer;
-
namespace Ocelot.DependencyInjection
{
using CacheManager.Core;
@@ -12,17 +8,14 @@ namespace Ocelot.DependencyInjection
using Ocelot.Authorisation;
using Ocelot.Cache;
using Ocelot.Claims;
- using Ocelot.Configuration.Authentication;
using Ocelot.Configuration.Creator;
using Ocelot.Configuration.File;
using Ocelot.Configuration.Parser;
- using Ocelot.Configuration.Provider;
using Ocelot.Configuration.Repository;
using Ocelot.Configuration.Setter;
using Ocelot.Configuration.Validator;
using Ocelot.DownstreamRouteFinder.Finder;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
- using Ocelot.DownstreamUrlCreator;
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
using Ocelot.Headers;
using Ocelot.Infrastructure.Claims.Parser;
@@ -44,14 +37,16 @@ namespace Ocelot.DependencyInjection
using System.Security.Cryptography.X509Certificates;
using IdentityServer4.AccessTokenValidation;
using Microsoft.AspNetCore.Builder;
- using Microsoft.AspNetCore.Hosting;
using Ocelot.Configuration;
- using Ocelot.Configuration.Builder;
- using FileConfigurationProvider = Ocelot.Configuration.Provider.FileConfigurationProvider;
using Microsoft.Extensions.DependencyInjection.Extensions;
- using System.Linq;
using System.Net.Http;
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
{
@@ -76,8 +71,8 @@ namespace Ocelot.DependencyInjection
_services.TryAddSingleton();
_services.TryAddSingleton();
_services.TryAddSingleton();
- _services.TryAddSingleton();
- _services.TryAddSingleton();
+ _services.TryAddSingleton();
+ _services.TryAddSingleton();
_services.TryAddSingleton();
_services.TryAddSingleton();
_services.TryAddSingleton();
@@ -89,9 +84,8 @@ namespace Ocelot.DependencyInjection
_services.TryAddSingleton();
_services.TryAddSingleton();
_services.TryAddSingleton();
- _services.TryAddSingleton();
- _services.TryAddSingleton();
- _services.TryAddSingleton();
+ _services.TryAddSingleton();
+ _services.TryAddSingleton();
_services.TryAddSingleton();
_services.TryAddSingleton();
_services.TryAddSingleton();
@@ -99,7 +93,6 @@ namespace Ocelot.DependencyInjection
_services.TryAddSingleton();
_services.TryAddSingleton();
_services.TryAddSingleton();
- _services.TryAddSingleton();
_services.TryAddSingleton();
_services.TryAddSingleton();
_services.TryAddSingleton();
@@ -121,6 +114,17 @@ namespace Ocelot.DependencyInjection
_services.TryAddSingleton();
_services.TryAddSingleton();
+ if (UsingEurekaServiceDiscoveryProvider(configurationRoot))
+ {
+ _services.AddDiscoveryClient(configurationRoot);
+ }
+ else
+ {
+ _services.TryAddSingleton();
+ }
+
+ _services.TryAddSingleton();
+
// see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc
// could maybe use a scoped data repository
_services.TryAddSingleton();
@@ -148,6 +152,12 @@ namespace Ocelot.DependencyInjection
// We add this here so that we can always inject something into the factory for IoC..
_services.AddSingleton();
+ _services.TryAddSingleton();
+ _services.TryAddSingleton();
+ _services.TryAddSingleton();
+ _services.TryAddSingleton();
+ _services.TryAddSingleton();
+ _services.TryAddSingleton();
}
public IOcelotAdministrationBuilder AddAdministration(string path, string secret)
@@ -182,6 +192,20 @@ namespace Ocelot.DependencyInjection
return new OcelotAdministrationBuilder(_services, _configurationRoot);
}
+ public IOcelotBuilder AddSingletonDefinedAggregator()
+ where T : class, IDefinedAggregator
+ {
+ _services.AddSingleton();
+ return this;
+ }
+
+ public IOcelotBuilder AddTransientDefinedAggregator()
+ where T : class, IDefinedAggregator
+ {
+ _services.AddTransient();
+ return this;
+ }
+
public IOcelotBuilder AddSingletonDelegatingHandler(bool global = false)
where THandler : DelegatingHandler
{
@@ -230,15 +254,6 @@ namespace Ocelot.DependencyInjection
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(config);
_services.AddSingleton();
_services.AddSingleton();
return this;
@@ -254,12 +269,12 @@ namespace Ocelot.DependencyInjection
_services.AddSingleton>(cacheManagerOutputCache);
_services.AddSingleton>(ocelotOutputCacheManager);
- var ocelotConfigCacheManagerOutputCache = CacheFactory.Build("OcelotConfigurationCache", settings);
- var ocelotConfigCacheManager = new OcelotCacheManagerCache