Remove Ocelot specific Middleware to make Ocelot more compatible with kestrel middleware and get ready for YARP

This commit is contained in:
Tom Pallister 2020-05-23 15:48:51 +01:00 committed by GitHub
parent 99a15d8668
commit fe3e8bd23a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
214 changed files with 9574 additions and 9919 deletions

View File

@ -5,5 +5,6 @@ end_of_line = lf
insert_final_newline = true insert_final_newline = true
[*.cs] [*.cs]
end_of_line = lf
indent_style = space indent_style = space
indent_size = 4 indent_size = 4

5
.gitattributes vendored Normal file
View File

@ -0,0 +1,5 @@
# 2010
*.txt -crlf
# 2020
*.txt text eol=lf

View File

@ -8,6 +8,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3FA7C349-DBE8-4904-A2CE-015B8869CE6C}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3FA7C349-DBE8-4904-A2CE-015B8869CE6C}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
.dockerignore = .dockerignore .dockerignore = .dockerignore
.editorconfig = .editorconfig
.gitignore = .gitignore .gitignore = .gitignore
build.cake = build.cake build.cake = build.cake
build.ps1 = build.ps1 build.ps1 = build.ps1

View File

@ -8,28 +8,16 @@
# Ocelot # Ocelot
Ocelot is a .NET API Gateway. This project is aimed at people using .NET running Ocelot is a .NET API Gateway. This project is aimed at people using .NET running a micro services / service oriented architecture
a micro services / service oriented architecture
that need a unified point of entry into their system. However it will work with anything that speaks HTTP and run on any platform that ASP.NET Core supports. that need a unified point of entry into their system. However it will work with anything that speaks HTTP and run on any platform that ASP.NET Core supports.
In particular I want easy integration with In particular I want easy integration with IdentityServer reference and bearer tokens.
IdentityServer reference and bearer tokens.
We have been unable to find this in my current workplace We have been unable to find this in my current workplacewithout having to write our own Javascript middlewares to handle the IdentityServer reference tokens. We would rather use the IdentityServer code that already existsto do this.
without having to write our own Javascript middlewares
to handle the IdentityServer reference tokens. We would
rather use the IdentityServer code that already exists
to do this.
Ocelot is a bunch of middlewares in a specific order. Ocelot is a bunch of middlewares in a specific order.
Ocelot manipulates the HttpRequest object into a state specified by its configuration until Ocelot manipulates the HttpRequest object into a state specified by its configuration until it reaches a request builder middleware where it creates a HttpRequestMessage object which is used to make a request to a downstream service. The middleware that makes the request is the last thing in the Ocelot pipeline. It does not call the next middleware. The response from the downstream service is retrieved as the requests goes back up the Ocelot pipeline. There is a piece of middleware that maps the HttpResponseMessage onto the HttpResponse object and that is returned to the client. That is basically it with a bunch of other features!
it reaches a request builder middleware where it creates a HttpRequestMessage object which is
used to make a request to a downstream service. The middleware that makes the request is
the last thing in the Ocelot pipeline. It does not call the next middleware.
The response from the downstream service is retrieved as the requests goes back up the Ocelot pipeline.
There is a piece of middleware that maps the HttpResponseMessage onto the HttpResponse object and that
is returned to the client. That is basically it with a bunch of other features!
## Features ## Features
@ -81,15 +69,13 @@ We love to receive contributions from the community so please keep them coming :
Pull requests, issues and commentary welcome! Pull requests, issues and commentary welcome!
Please complete the relevant template for issues and PRs. Sometimes it's worth getting in touch with us to discuss changes Please complete the relevant template for issues and PRs. Sometimes it's worth getting in touch with us to discuss changes before doing any work incase this is something we are already doing or it might not make sense. We can also give advice on the easiest way to do things :)
before doing any work incase this is something we are already doing or it might not make sense. We can also give
advice on the easiest way to do things :)
Finally we mark all existing issues as help wanted, small, medium and large effort. If you want to contribute for the first time I suggest looking at a help wanted & small effort issue :) Finally we mark all existing issues as help wanted, small, medium and large effort. If you want to contribute for the first time I suggest looking at a help wanted & small effort issue :)
## Donate ## Donate
If you think this project is worth supporting financially please make a contribution using the button below! If you think this project is worth supporting financially please make a contribution using the button below! We use the money to run the https://threemammals.com website.
[![Support via PayPal](https://cdn.rawgit.com/twolfson/paypal-github-button/1.0.0/dist/button.svg)](https://www.paypal.me/ThreeMammals/) [![Support via PayPal](https://cdn.rawgit.com/twolfson/paypal-github-button/1.0.0/dist/button.svg)](https://www.paypal.me/ThreeMammals/)

View File

@ -1,9 +1,7 @@
Administration Administration
============== ==============
Ocelot supports changing configuration during runtime via an authenticated HTTP API. This can be authenticated in two ways either using Ocelot's Ocelot supports changing configuration during runtime via an authenticated HTTP API. This can be authenticated in two ways either using Ocelot's internal IdentityServer (for authenticating requests to the administration API only) or hooking the administration API authentication into your own IdentityServer.
internal IdentityServer (for authenticating requests to the administration API only) or hooking the administration API authentication into your own
IdentityServer.
The first thing you need to do if you want to use the administration API is bring in the relavent NuGet package.. The first thing you need to do if you want to use the administration API is bring in the relavent NuGet package..
@ -14,6 +12,8 @@ This will bring down everything needed by the admin API.
Providing your own IdentityServer Providing your own IdentityServer
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
All you need to do to hook into your own IdentityServer is add the following to your ConfigureServices method. All you need to do to hook into your own IdentityServer is add the following to your ConfigureServices method.
.. code-block:: csharp .. code-block:: csharp
@ -33,22 +33,16 @@ All you need to do to hook into your own IdentityServer is add the following to
You now need to get a token from your IdentityServer and use in subsequent requests to Ocelot's administration API. You now need to get a token from your IdentityServer and use in subsequent requests to Ocelot's administration API.
This feature was implemented for `issue 228 <https://github.com/ThreeMammals/Ocelot/issues/228>`_. It is useful because the IdentityServer authentication This feature was implemented for `issue 228 <https://github.com/ThreeMammals/Ocelot/issues/228>`_. It is useful because the IdentityServer authentication middleware needs the URL of the IdentityServer. If you are using the internal IdentityServer it might not alaways be possible to have the Ocelot URL.
middleware needs the URL of the IdentityServer. If you are using the internal IdentityServer it might not alaways be possible to have the Ocelot URL.
Internal IdentityServer Internal IdentityServer
^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
The API is authenticated using bearer tokens that you request from Ocelot iteself. This is provided by the amazing The API is authenticated using bearer tokens that you request from Ocelot iteself. This is provided by the amazing `Identity Server <https://github.com/IdentityServer/IdentityServer4>`_ project that I have been using for a few years now. Check them out.
`Identity Server <https://github.com/IdentityServer/IdentityServer4>`_ project that I have been using for a few years now. Check them out.
In order to enable the administration section you need to do a few things. First of all add this to your In order to enable the administration section you need to do a few things. First of all add this to yourinitial Startup.cs.
initial Startup.cs.
The path can be anything you want and it is obviously reccomended don't use The path can be anything you want and it is obviously reccomended don't usea url you would like to route through with Ocelot as this will not work. The administration uses theMapWhen functionality of asp.net core and all requests to {root}/administration will be sent there not to the Ocelot middleware.
a url you would like to route through with Ocelot as this will not work. The administration uses the
MapWhen functionality of asp.net core and all requests to {root}/administration will be sent there not
to the Ocelot middleware.
The secret is the client secret that Ocelot's internal IdentityServer will use to authenticate requests to the administration API. This can be whatever you want it to be! The secret is the client secret that Ocelot's internal IdentityServer will use to authenticate requests to the administration API. This can be whatever you want it to be!
@ -61,9 +55,7 @@ The secret is the client secret that Ocelot's internal IdentityServer will use t
.AddAdministration("/administration", "secret"); .AddAdministration("/administration", "secret");
} }
In order for the administration API to work, Ocelot / IdentityServer must be able to call itself for validation. This means that you need to add the base url of Ocelot In order for the administration API to work, Ocelot / IdentityServer must be able to call itself for validation. This means that you need to add the base url of Ocelot to global configuration if it is not default (http://localhost:5000). Please note if you are using something like docker to host Ocelot it might not be able to call back to localhost etc and you need to know what you are doing with docker networking in this scenario. Anyway this can be done as follows..
to global configuration if it is not default (http://localhost:5000). Please note if you are using something like docker to host Ocelot it might not be able to
call back to localhost etc and you need to know what you are doing with docker networking in this scenario. Anyway this can be done as follows..
If you want to run on a different host and port locally.. If you want to run on a different host and port locally..
@ -81,13 +73,10 @@ or if Ocelot is exposed via dns
"BaseUrl": "http://mydns.com" "BaseUrl": "http://mydns.com"
} }
Now if you went with the configuration options above and want to access the API you can use the postman scripts Now if you went with the configuration options above and want to access the API you can use the postman scriptscalled ocelot.postman_collection.json in the solution to change the Ocelot configuration. Obviously these will need to be changed if you are running Ocelot on a different url to http://localhost:5000.
called ocelot.postman_collection.json in the solution to change the Ocelot configuration. Obviously these
will need to be changed if you are running Ocelot on a different url to http://localhost:5000.
The scripts show you how to request a bearer token from ocelot and then use it to GET the existing configuration and POST The scripts show you how to request a bearer token from ocelot and then use it to GET the existing configuration and POST a configuration.
a configuration.
If you are running multiple Ocelot instances in a cluster then you need to use a certificate to sign the bearer tokens used to access the administration API. If you are running multiple Ocelot instances in a cluster then you need to use a certificate to sign the bearer tokens used to access the administration API.
@ -98,7 +87,7 @@ In order to do this you need to add two more environmental variables for each Oc
``OCELOT_CERTIFICATE_PASSWORD`` ``OCELOT_CERTIFICATE_PASSWORD``
The password for the certificate. The password for the certificate.
Normally Ocelot just uses temporary signing credentials but if you set these environmental variables then it will use the certificate. If all the other Ocelot instances in the cluster have the same certificate then you are good! Normally Ocelot just uses temporary signing credentials but if you set these environmental variables then it will use the certificate. If all the other Ocelot instances in thecluster have the same certificate then you are good!
Administration API Administration API
@ -130,8 +119,7 @@ This overrwrites the existing configuration (should probably be a put!). I recco
The body of the request is JSON and it is the same format as the FileConfiguration.cs that we use to set up The body of the request is JSON and it is the same format as the FileConfiguration.cs that we use to set up
Ocelot on a file system. Ocelot on a file system.
Please note that if you want to use this API then the process running Ocelot must have permission to write to the disk Please note that if you want to use this API then the process running Ocelot must have permission to write to the disk where your ocelot.json or ocelot.{environment}.json is located. This is because Ocelot will overwrite them on save.
where your ocelot.json or ocelot.{environment}.json is located. This is because Ocelot will overwrite them on save.
**DELETE {adminPath}/outputcache/{region}** **DELETE {adminPath}/outputcache/{region}**

View File

@ -16,8 +16,7 @@ In order to authenticate ReRoutes and subsequently use any of Ocelot's claims ba
} }
In this example TestKey is the scheme that this provider has been registered with. In this example TestKey is the scheme that this provider has been registered with. We then map this to a ReRoute in the configuration e.g.
We then map this to a ReRoute in the configuration e.g.
.. code-block:: json .. code-block:: json
@ -39,9 +38,7 @@ We then map this to a ReRoute in the configuration e.g.
} }
}] }]
When Ocelot runs it will look at this ReRoutes AuthenticationOptions.AuthenticationProviderKey When Ocelot runs it will look at this ReRoutes AuthenticationOptions.AuthenticationProviderKey and check that there is an Authentication provider registered with the given key. If there isn't then Ocelot will not start up, if there is then the ReRoute will use that provider when it executes.
and check that there is an Authentication provider registered with the given key. If there isn't then Ocelot
will not start up, if there is then the ReRoute will use that provider when it executes.
If a ReRoute is authenticated Ocelot will invoke whatever scheme is associated with it while executing the authentication middleware. If the request fails authentication Ocelot returns a http status code 401. If a ReRoute is authenticated Ocelot will invoke whatever scheme is associated with it while executing the authentication middleware. If the request fails authentication Ocelot returns a http status code 401.

View File

@ -1,8 +1,7 @@
Authorisation Authorisation
============= =============
Ocelot supports claims based authorisation which is run post authentication. This means if Ocelot supports claims based authorisation which is run post authentication. This means if you have a route you want to authorise you can add the following to you ReRoute configuration.
you have a route you want to authorise you can add the following to you ReRoute configuration.
.. code-block:: json .. code-block:: json
@ -10,9 +9,7 @@ you have a route you want to authorise you can add the following to you ReRoute
"UserType": "registered" "UserType": "registered"
} }
In this example when the authorisation middleware is called Ocelot will check to see In this example when the authorisation middleware is called Ocelot will check to seeif the user has the claim type UserType and if the value of that claim is registered. If it isn't then the user will not be authorised and the response will be 403 forbidden.
if the user has the claim type UserType and if the value of that claim is registered.
If it isn't then the user will not be authorised and the response will be 403 forbidden.

View File

@ -1,10 +1,7 @@
Caching Caching
======= =======
Ocelot supports some very rudimentary caching at the moment provider by Ocelot supports some very rudimentary caching at the moment provider by the `CacheManager <https://github.com/MichaCo/CacheManager>`_ project. This is an amazing project that is solving a lot of caching problems. I would reccomend using this package to cache with Ocelot.
the `CacheManager <https://github.com/MichaCo/CacheManager>`_ project. This is an amazing project
that is solving a lot of caching problems. I would reccomend using this package to
cache with Ocelot.
The following example shows how to add CacheManager to Ocelot so that you can do output caching. The following example shows how to add CacheManager to Ocelot so that you can do output caching.
@ -32,19 +29,14 @@ Finally in order to use caching on a route in your ReRoute configuration add thi
In this example ttl seconds is set to 15 which means the cache will expire after 15 seconds. In this example ttl seconds is set to 15 which means the cache will expire after 15 seconds.
If you look at the example `here <https://github.com/ThreeMammals/Ocelot/blob/master/test/Ocelot.ManualTest/Program.cs>`_ you can see how the cache manager If you look at the example `here <https://github.com/ThreeMammals/Ocelot/blob/master/test/Ocelot.ManualTest/Program.cs>`_ you can see how the cache manager is setup and then passed into the Ocelot AddCacheManager configuration method. You can use any settings supported by the CacheManager package and just pass them in.
is setup and then passed into the Ocelot AddCacheManager configuration method. You can use any settings supported by
the CacheManager package and just pass them in.
Anyway Ocelot currently supports caching on the URL of the downstream service Anyway Ocelot currently supports caching on the URL of the downstream service and setting a TTL in seconds to expire the cache. You can also clear the cache for a region by calling Ocelot's administration API.
and setting a TTL in seconds to expire the cache. You can also clear the cache for a region
by calling Ocelot's administration API.
Your own caching Your own caching
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
If you want to add your own caching method implement the following interfaces and register them in DI If you want to add your own caching method implement the following interfaces and register them in DI e.g. ``services.AddSingleton<IOcelotCache<CachedResponse>, MyCache>()``
e.g. ``services.AddSingleton<IOcelotCache<CachedResponse>, MyCache>()``
``IOcelotCache<CachedResponse>`` this is for output caching. ``IOcelotCache<CachedResponse>`` this is for output caching.

View File

@ -1,35 +1,17 @@
Claims Transformation Claims Transformation
===================== =====================
Ocelot allows the user to access claims and transform them into headers, query string Ocelot allows the user to access claims and transform them into headers, query string parameters, other claims and change downstream paths. This is only available once a user has been authenticated.
parameters, other claims and change downstream paths. This is only available once a user
has been authenticated.
After the user is authenticated we run the claims to claims transformation middleware. After the user is authenticated we run the claims to claims transformation middleware. This allows the user to transform claims before the authorisation middleware is called. After the user is authorised first we call the claims to headers middleware, thenthe claims to query string parameters middleware, and Finally the claims to downstream pathmiddleware.
This allows the user to transform claims before the authorisation middleware is called.
After the user is authorised first we call the claims to headers middleware, then
the claims to query string parameters middleware, and Finally the claims to downstream path
middleware.
The syntax for performing the transforms is the same for each process. In the ReRoute The syntax for performing the transforms is the same for each process. In the ReRoute configuration a json dictionary is added with a specific name either AddClaimsToRequest, AddHeadersToRequest, AddQueriesToRequest, or ChangeDownstreamPathTemplate.
configuration a json dictionary is added with a specific name either AddClaimsToRequest,
AddHeadersToRequest, AddQueriesToRequest, or ChangeDownstreamPathTemplate.
Note: I'm not a hotshot programmer so have no idea if this syntax is good... Note: I'm not a hotshot programmer so have no idea if this syntax is good...
Within this dictionary the entries specify how Ocelot should transform things! Within this dictionary the entries specify how Ocelot should transform things! The key to the dictionary is going to become the key of either a claim, header or query parameter. In the case of ChangeDownstreamPathTemplate, the key must be also specified in the DownstreamPathTemplate, in order to do the transformation.
The key to the dictionary is going to become the key of either a claim, header
or query parameter. In the case of ChangeDownstreamPathTemplate, the key must be
also specified in the DownstreamPathTemplate, in order to do the transformation.
The value of the entry is parsed to logic that will perform the transform. First of The value of the entry is parsed to logic that will perform the transform. First ofall a dictionary accessor is specified e.g. Claims[CustomerId]. This means we want to access the claims and get the CustomerId claim type. Next is a greater than (>)symbol which is just used to split the string. The next entry is either value or value with an indexer. If value is specified Ocelot will just take the value and add it to the transform. If the value has an indexer Ocelot will look for a delimiter which is provided after another greater than symbol. Ocelot will then split the value on the delimiter and add whatever was at the index requested to the transform.
all a dictionary accessor is specified e.g. Claims[CustomerId]. This means we want
to access the claims and get the CustomerId claim type. Next is a greater than (>)
symbol which is just used to split the string. The next entry is either value or value with
and indexer. If value is specified Ocelot will just take the value and add it to the
transform. If the value has an indexer Ocelot will look for a delimiter which is provided
after another greater than symbol. Ocelot will then split the value on the delimiter
and add whatever was at the index requested to the transform.
Claims to Claims Transformation Claims to Claims Transformation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -43,8 +25,7 @@ Below is an example configuration that will transforms claims to claims
"UserId": "Claims[sub] > value[1] > |" "UserId": "Claims[sub] > value[1] > |"
} }
This shows a transforms where Ocelot looks at the users sub claim and transforms it into This shows a transforms where Ocelot looks at the users sub claim and transforms it into UserType and UserId claims. Assuming the sub looks like this "usertypevalue|useridvalue".
UserType and UserId claims. Assuming the sub looks like this "usertypevalue|useridvalue".
Claims to Headers Tranformation Claims to Headers Tranformation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -57,8 +38,7 @@ Below is an example configuration that will transforms claims to headers
"CustomerId": "Claims[sub] > value[1] > |" "CustomerId": "Claims[sub] > value[1] > |"
} }
This shows a transform where Ocelot looks at the users sub claim and transforms it into a This shows a transform where Ocelot looks at the users sub claim and transforms it into a CustomerId header. Assuming the sub looks like this "usertypevalue|useridvalue".
CustomerId header. Assuming the sub looks like this "usertypevalue|useridvalue".
Claims to Query String Parameters Transformation Claims to Query String Parameters Transformation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -71,8 +51,7 @@ Below is an example configuration that will transforms claims to query string pa
"LocationId": "Claims[LocationId] > value", "LocationId": "Claims[LocationId] > value",
} }
This shows a transform where Ocelot looks at the users LocationId claim and add it as This shows a transform where Ocelot looks at the users LocationId claim and add it as a query string parameter to be forwarded onto the downstream service.
a query string parameter to be forwarded onto the downstream service.
Claims to Downstream Path Transformation Claims to Downstream Path Transformation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -87,10 +66,7 @@ Below is an example configuration that will transform claims to downstream path
"userId": "Claims[sub] > value[1] > |", "userId": "Claims[sub] > value[1] > |",
} }
This shows a transform where Ocelot looks at the users userId claim and substitutes the value This shows a transform where Ocelot looks at the users userId claim and substitutes the value to the "{userId}" placeholder specified in the DownstreamPathTemplate. Take into account that the key specified in the ChangeDownstreamPathTemplate must be the same than the placeholder specified in
to the "{userId}" placeholder specified in the DownstreamPathTemplate. Take into account that the
key specified in the ChangeDownstreamPathTemplate must be the same than the placeholder specified in
the DownstreamPathTemplate. the DownstreamPathTemplate.
Note: if a key specified in the ChangeDownstreamPathTemplate does not exist as a placeholder in DownstreamPathTemplate Note: if a key specified in the ChangeDownstreamPathTemplate does not exist as a placeholder in DownstreamPathTemplate it will fail at runtime returning an error in the response.
it will fail at runtime returning an error in the response.

View File

@ -1,11 +1,7 @@
Configuration Configuration
============ ============
An example configuration can be found `here <https://github.com/ThreeMammals/Ocelot/blob/master/test/Ocelot.ManualTest/ocelot.json>`_. An example configuration can be found `here <https://github.com/ThreeMammals/Ocelot/blob/master/test/Ocelot.ManualTest/ocelot.json>`_. 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 if you don't want to manage lots of ReRoute specific settings.
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
if you don't want to manage lots of ReRoute specific settings.
.. code-block:: json .. code-block:: json
@ -152,8 +148,7 @@ Then you add the following when you register your services Ocelot will attempt t
.AddConsul() .AddConsul()
.AddConfigStoredInConsul(); .AddConfigStoredInConsul();
You also need to add the following to your ocelot.json. This is how Ocelot You also need to add the following to your ocelot.json. This is how Ocelot finds your Consul agent and interacts to load and store the configuration from Consul.
finds your Consul agent and interacts to load and store the configuration from Consul.
.. code-block:: json .. code-block:: json
@ -164,8 +159,7 @@ 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 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.
I guess it means if you want to use Ocelot to its fullest you take on Consul as a dependency for now.
This feature has a 3 second ttl cache before making a new request to your local consul agent. This feature has a 3 second ttl cache before making a new request to your local consul agent.
@ -203,17 +197,9 @@ Follow Redirects / Use CookieContainer
Use HttpHandlerOptions in ReRoute configuration to set up HttpHandler behavior: 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 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.
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 2. UseCookieContainer is a value that indicates whether the handler uses the CookieContainer property to store server cookies and uses these cookies when sending requests. The default value is false. Please note that if you are using the CookieContainer Ocelot caches the HttpClient for each downstream service. This means that all requests to that DownstreamService will share the same cookies. `Issue 274 <https://github.com/ThreeMammals/Ocelot/issues/274>`_ was created because a user noticed that the cookies were being shared. I tried to think of a nice way to handle this but I think it is impossible. If you don't cache the clients that means each request gets a new client and therefore a new cookie container. If you clear the cookies from the cached client container you get race conditions due to inflight requests. This would also mean that subsequent requests don't 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!
property to store server cookies and uses these cookies when sending requests. The default value is false. Please note
that if you are using the CookieContainer Ocelot caches the HttpClient for each downstream service. This means that all requests
to that DownstreamService will share the same cookies. `Issue 274 <https://github.com/ThreeMammals/Ocelot/issues/274>`_ was created because a user
noticed that the cookies were being shared. I tried to think of a nice way to handle this but I think it is impossible. If you don't cache the clients
that means each request gets a new client and therefore a new cookie container. If you clear the cookies from the cached client container you get race conditions due to inflight
requests. This would also mean that subsequent requests don't 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 SSL Errors
^^^^^^^^^^ ^^^^^^^^^^

View File

@ -31,8 +31,7 @@ Next you must add the handlers to Ocelot's container like below...
.AddDelegatingHandler<FakeHandler>() .AddDelegatingHandler<FakeHandler>()
.AddDelegatingHandler<FakeHandlerTwo>() .AddDelegatingHandler<FakeHandlerTwo>()
Both of these Add methods have a default parameter called global which is set to false. If it is false then the intent of Both of these Add methods have a default parameter called global which is set to false. If it is false then the intent of the DelegatingHandler is to be applied to specific ReRoutes via ocelot.json (more on that later). If it is set to true
the DelegatingHandler is to be applied to specific ReRoutes via ocelot.json (more on that later). If it is set to true
then it becomes a global handler and will be applied to all ReRoutes. then it becomes a global handler and will be applied to all ReRoutes.
e.g. e.g.
@ -44,8 +43,7 @@ As below...
services.AddOcelot() services.AddOcelot()
.AddDelegatingHandler<FakeHandler>(true) .AddDelegatingHandler<FakeHandler>(true)
Finally if you want ReRoute specific DelegatingHandlers or to order your specific and / or global (more on this later) DelegatingHandlers Finally if you want ReRoute specific DelegatingHandlers or to order your specific and / or global (more on this later) DelegatingHandlers then you must add the following json to the specific ReRoute in ocelot.json. The names in the array must match the class names of your
then you must add the following json to the specific ReRoute in ocelot.json. The names in the array must match the class names of your
DelegatingHandlers for Ocelot to match them together. DelegatingHandlers for Ocelot to match them together.
.. code-block:: json .. code-block:: json

View File

@ -1,14 +1,11 @@
GraphQL 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 OK you got me Ocelot doesn't directly support GraphQL but so many people have asked about it I wanted to show how easy it is to integrate the `graphql-dotnet <https://github.com/graphql-dotnet/graphql-dotnet>`_ library.
the `graphql-dotnet <https://github.com/graphql-dotnet/graphql-dotnet>`_ library.
Please see the sample project `OcelotGraphQL <https://github.com/ThreeMammals/Ocelot/tree/master/samples/OcelotGraphQL>`_. Please see the sample project `OcelotGraphQL <https://github.com/ThreeMammals/Ocelot/tree/master/samples/OcelotGraphQL>`_. Using a combination of the graphql-dotnet project and Ocelot's DelegatingHandler features this is pretty easy to do.
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!
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 :> Good luck and have fun :>

View File

@ -136,8 +136,7 @@ An example of using {RemoteIpAddress} placeholder...
Future Future
^^^^^^ ^^^^^^
Ideally this feature would be able to support the fact that a header can have multiple values. At the moment it just assumes one. Ideally this feature would be able to support the fact that a header can have multiple values. At the moment it just assumes one. It would also be nice if it could multi find and replace e.g.
It would also be nice if it could multi find and replace e.g.
.. code-block:: json .. code-block:: json

View File

@ -30,8 +30,7 @@ You can replicate a Permissive. Using RBAC role bindings.
.. code-block::bash .. code-block::bash
kubectl create clusterrolebinding permissive-binding --clusterrole=cluster-admin --user=admin --user=kubelet --group=system:serviceaccounts kubectl create clusterrolebinding permissive-binding --clusterrole=cluster-admin --user=admin --user=kubelet --group=system:serviceaccounts
The following example shows how to set up a ReRoute that will work in kubernetes. The most important thing is the ServiceName which is made up of the The following example shows how to set up a ReRoute that will work in kubernetes. The most important thing is the ServiceName which is made up of the kubernetes service name. We also need to set up the ServiceDiscoveryProvider in GlobalConfiguration. The example here shows a typical configuration.
kubernetes service name. We also need to set up the ServiceDiscoveryProvider in GlobalConfiguration. The example here shows a typical configuration.
.. code-block:: json .. code-block:: json
@ -75,8 +74,7 @@ You use Ocelot to poll kubernetes for latest service information rather than per
The polling interval is in milliseconds and tells Ocelot how often to call kubernetes for changes in service configuration. The polling interval is in milliseconds and tells Ocelot how often to call kubernetes for changes in service configuration.
Please note there are tradeoffs here. If you poll kubernetes it is possible Ocelot will not know if a service is down depending on your polling interval and you might get more errors than if you get the latest services per request. This really depends on how volatile your services are. I doubt it will matter for most people and polling may give a tiny performance improvement over calling kubernetes per request. Please note there are tradeoffs here. If you poll kubernetes it is possible Ocelot will not know if a service is down depending on your polling interval and you might get more errors than if you get the latest services per request. This really depends on how volatile your services are. I doubt it will matter for most people and polling may give a tiny performance improvement over calling kubernetes per request. There is no way for Ocelot to work these out for you.
There is no way for Ocelot to work these out for you.
If your downstream service resides in a different namespace you can override the global setting at the ReRoute level by specifying a ServiceNamespace. If your downstream service resides in a different namespace you can override the global setting at the ReRoute level by specifying a ServiceNamespace.

View File

@ -67,10 +67,7 @@ service discovery provider (consul) then Ocelot should respect this and stop cal
CookieStickySessions CookieStickySessions
^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^
I've implemented a really basic sticky session type of load balancer. The scenario it is meant to support is you have a bunch of downstream I've implemented a really basic sticky session type of load balancer. The scenario it is meant to support is you have a bunch of downstream servers that don't share session state so if you get more than one request for one of these servers then it should go to the same box each time or the session state might be incorrect for the given user. This feature was requested in `Issue #322 <https://github.com/ThreeMammals/Ocelot/issues/322>`_ though what the user wants is more complicated than just sticky sessions :) anyway I thought this would be a nice feature to have!
servers that don't share session state so if you get more than one request for one of these servers then it should go to the same box each
time or the session state might be incorrect for the given user. This feature was requested in `Issue #322 <https://github.com/ThreeMammals/Ocelot/issues/322>`_
though what the user wants is more complicated than just sticky sessions :) anyway I thought this would be a nice feature to have!
In order to set up CookieStickySessions load balancer you need to do something like the following. In order to set up CookieStickySessions load balancer you need to do something like the following.
@ -98,24 +95,18 @@ In order to set up CookieStickySessions load balancer you need to do something l
"UpstreamHttpMethod": [ "Put", "Delete" ] "UpstreamHttpMethod": [ "Put", "Delete" ]
} }
The LoadBalancerOptions are Type this needs to be CookieStickySessions, Key this is the key of the cookie you The LoadBalancerOptions are Type this needs to be CookieStickySessions, Key this is the key of the cookie you wish to use for the sticky sessions, Expiry this is how long in milliseconds you want to the session to be stuck for. Remember this refreshes on every request which is meant to mimick how sessions work usually.
wish to use for the sticky sessions, Expiry this is how long in milliseconds you want to the session to be stuck for. Remember this
refreshes on every request which is meant to mimick how sessions work usually.
If you have multiple ReRoutes with the same LoadBalancerOptions then all of those ReRoutes will use the same load balancer for there If you have multiple ReRoutes with the same LoadBalancerOptions then all of those ReRoutes will use the same load balancer for there subsequent requests. This means the sessions will be stuck across ReRoutes.
subsequent requests. This means the sessions will be stuck across ReRoutes.
Please note that if you give more than one DownstreamHostAndPort or you are using a Service Discovery provider such as Consul Please note that if you give more than one DownstreamHostAndPort or you are using a Service Discovery provider such as Consul and this returns more than one service then CookieStickySessions uses round robin to select the next server. This is hard coded at the moment but could be changed.
and this returns more than one service then CookieStickySessions uses round robin to select the next server. This is hard coded at the
moment but could be changed.
Custom Load Balancers Custom Load Balancers
^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^
`DavidLievrouw <https://github.com/DavidLievrouw`_ implemented a way to provide Ocelot with custom load balancer in `PR 1155 <https://github.com/ThreeMammals/Ocelot/pull/1155`_. `DavidLievrouw <https://github.com/DavidLievrouw`_ implemented a way to provide Ocelot with custom load balancer in `PR 1155 <https://github.com/ThreeMammals/Ocelot/pull/1155`_.
In order to create and use a custom load balancer you can do the following. Below we setup a basic load balancing config and not the Type is CustomLoadBalancer this is the name of a class we will In order to create and use a custom load balancer you can do the following. Below we setup a basic load balancing config and not the Type is CustomLoadBalancer this is the name of a class we will setup to do load balancing.
setup to do load balancing.
.. code-block:: json .. code-block:: json
@ -156,7 +147,7 @@ Then you need to create a class that implements the ILoadBalancer interface. Bel
_services = services; _services = services;
} }
public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext downstreamContext) public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext downstreamContext, HttpContext httpContext)
{ {
var services = await _services(); var services = await _services();
lock (_lock) lock (_lock)

View File

@ -1,20 +1,17 @@
Logging Logging
======= =======
Ocelot uses the standard logging interfaces ILoggerFactory / ILogger<T> at the moment. Ocelot uses the standard logging interfaces ILoggerFactory / ILogger<T> at the moment. This is encapsulated in IOcelotLogger / IOcelotLoggerFactory with an implementation
This is encapsulated in IOcelotLogger / IOcelotLoggerFactory with an implementation
for the standard asp.net core logging stuff at the moment. This is because Ocelot add's some extra info to the logs such as request id if it is configured. for the standard asp.net core logging stuff at the moment. This is because Ocelot add's some extra info to the logs such as request id if it is configured.
There is a global error handler that should catch any exceptions thrown and log them as errors. There is a global error handler that should catch any exceptions thrown and log them as errors.
Finally if logging is set to trace level Ocelot will log starting, finishing and any middlewares that throw an exception which can be quite useful. Finally if logging is set to trace level Ocelot will log starting, finishing and any middlewares that throw an exception which can be quite useful.
The reason for not just using bog standard framework logging is that I could not The reason for not just using bog standard framework logging is that I could not work out how to override the request id that get's logged when setting IncludeScopes
work out how to override the request id that get's logged when setting IncludeScopes
to true for logging settings. Nicely onto the next feature. to true for logging settings. Nicely onto the next feature.
Warning Warning
^^^^^^^ ^^^^^^^
If you are logging to Console you will get terrible performance. I have had so many issues about performance issues with Ocelot 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 :)
and it is always logging level Debug, logging to Console :) Make sure you are logging to something proper in production :)

View File

@ -1,8 +1,7 @@
Quality of Service Quality of Service
================== ==================
Ocelot supports one QoS capability at the current time. You can set on a per ReRoute basis if you Ocelot supports one QoS capability at the current time. You can set on a per ReRoute basis if you want to use a circuit breaker when making requests to a downstream service. This uses an awesome
want to use a circuit breaker when making requests to a downstream service. This uses an awesome
.NET library called Polly check them out `here <https://github.com/App-vNext/Polly>`_. .NET library called Polly check them out `here <https://github.com/App-vNext/Polly>`_.
The first thing you need to do if you want to use the administration API is bring in the relevant NuGet package.. The first thing you need to do if you want to use the administration API is bring in the relevant NuGet package..
@ -30,8 +29,7 @@ Then add the following section to a ReRoute configuration.
"TimeoutValue":5000 "TimeoutValue":5000
} }
You must set a number greater than 0 against ExceptionsAllowedBeforeBreaking for this rule to be You must set a number greater than 0 against ExceptionsAllowedBeforeBreaking for this rule to be implemented. Duration of break means the circuit breaker will stay open for 1 second after it is tripped.
implemented. Duration of break means the circuit breaker will stay open for 1 second after it is tripped.
TimeoutValue means if 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.
You can set the TimeoutValue in isolation of the ExceptionsAllowedBeforeBreaking and DurationOfBreak options. You can set the TimeoutValue in isolation of the ExceptionsAllowedBeforeBreaking and DurationOfBreak options.
@ -44,5 +42,4 @@ You can set the TimeoutValue in isolation of the ExceptionsAllowedBeforeBreaking
There is no point setting the other two in isolation as they affect each other :) 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 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.
on all downstream requests. If someone needs this to be configurable open an issue.

View File

@ -103,12 +103,10 @@ In order to make an Aggregator you must implement this interface.
public interface IDefinedAggregator public interface IDefinedAggregator
{ {
Task<DownstreamResponse> Aggregate(List<DownstreamResponse> responses); Task<DownstreamResponse> Aggregate(List<HttpContext> 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 With this feature you can pretty much do whatever you want because the HttpContext objects contain the results of all the aggregate requests. Please note if the HttpClient throws an exception when making a request to a ReRoute in the aggregate then you will not get a HttpContext for it but you would for any that succeed. If it does throw an exception this will be logged.
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 Basic expecting JSON from Downstream Services
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -1,12 +1,10 @@
Request Id / Correlation Id Request Id / Correlation Id
=========================== ===========================
Ocelot supports a client sending a request id in the form of a header. If set Ocelot will Ocelot supports a client sending a request id in the form of a header. If set Ocelot willuse the requestid for logging as soon as it becomes available in the middleware pipeline.
use the requestid for logging as soon as it becomes available in the middleware pipeline.
Ocelot will also forward the request id with the specified header to the downstream service. Ocelot will also forward the request id with the specified header to the downstream service.
You can still get the asp.net core request id in the logs if you set You can still get the asp.net core request id in the logs if you set IncludeScopes true in your logging config.
IncludeScopes true in your logging config.
In order to use the request id feature you have two options. In order to use the request id feature you have two options.

View File

@ -1,12 +1,10 @@
Routing Routing
======= =======
Ocelot's primary functionality is to take incoming http requests and forward them on Ocelot's primary functionality is to take incoming http requests and forward them on to a downstream service. Ocelot currently only supports this in the form of another http request (in the future
to a downstream service. Ocelot currently only supports this in the form of another http request (in the future
this could be any transport mechanism). this could be any transport mechanism).
Ocelot's describes the routing of one request to another as a ReRoute. In order to get Ocelot's describes the routing of one request to another as a ReRoute. In order to get anything working in Ocelot you need to set up a ReRoute in the configuration.
anything working in Ocelot you need to set up a ReRoute in the configuration.
.. code-block:: json .. code-block:: json
@ -34,14 +32,11 @@ To configure a ReRoute you need to add one to the ReRoutes json array.
The DownstreamPathTemplate, DownstreamScheme and DownstreamHostAndPorts define the URL that a request will be forwarded to. The DownstreamPathTemplate, DownstreamScheme and DownstreamHostAndPorts define the URL that a request will be forwarded to.
DownstreamHostAndPorts is a collection that defines the host and port of any downstream services that you wish to forward requests to. DownstreamHostAndPorts is a collection that defines the host and port of any downstream services that you wish to forward requests to. Usually this will just contain a single entry but sometimes you might want to load balance requests to your downstream services and Ocelot allows you add more than one entry and then select a load balancer.
Usually this will just contain a single entry but sometimes you might want to load balance requests to your downstream services and Ocelot allows you add more than one entry and then select a load balancer.
The UpstreamPathTemplate is the URL that Ocelot will use to identify which DownstreamPathTemplate to use for a given request. The UpstreamPathTemplate is the URL that Ocelot will use to identify which DownstreamPathTemplate to use for a given request. The UpstreamHttpMethod is used so Ocelot can distinguish between requests with different HTTP verbs to the same URL. You can set a specific list of HTTP Methods or set an empty list to allow any of them.
The UpstreamHttpMethod is used so Ocelot can distinguish between requests with different HTTP verbs to the same URL. You can set a specific list of HTTP Methods or set an empty list to allow any of them.
In Ocelot you can add placeholders for variables to your Templates in the form of {something}. In Ocelot you can add placeholders for variables to your Templates in the form of {something}. The placeholder variable needs to be present in both the DownstreamPathTemplate and UpstreamPathTemplate properties. When it is Ocelot will attempt to substitute the value in the UpstreamPathTemplate placeholder into the DownstreamPathTemplate for each request Ocelot processes.
The placeholder variable needs to be present in both the DownstreamPathTemplate and UpstreamPathTemplate properties. When it is Ocelot will attempt to substitute the value in the UpstreamPathTemplate placeholder into the DownstreamPathTemplate for each request Ocelot processes.
You can also do a catch all type of ReRoute e.g. You can also do a catch all type of ReRoute e.g.
@ -154,8 +149,7 @@ See `Issue 270 <https://github.com/ThreeMammals/Ocelot/pull/270>`_ for reference
"Priority": 0 "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 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.
to set any priority you wish.
e.g. you could have e.g. you could have
@ -175,8 +169,7 @@ and
"Priority": 1 "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 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!).
matched /goods/{catchAll} (because this is the first ReRoute in the list!).
Dynamic Routing Dynamic Routing
^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^

View File

@ -3,10 +3,8 @@
Service Discovery Service Discovery
================= =================
Ocelot allows you to specify a service discovery provider and will use this to find the host and port Ocelot allows you to specify a service discovery provider and will use this to find the host and port for the downstream service Ocelot is forwarding a request to. At the moment this is only supported in the
for the downstream service Ocelot is forwarding a request to. At the moment this is only supported in the GlobalConfiguration section which means the same service discovery provider will be used for all ReRoutes you specify a ServiceName for at ReRoute level.
GlobalConfiguration section which means the same service discovery provider will be used for all ReRoutes
you specify a ServiceName for at ReRoute level.
Consul Consul
^^^^^^ ^^^^^^
@ -38,9 +36,7 @@ Please note the Scheme option defauls to HTTP. It was added in this `PR <https:/
In the future we can add a feature that allows ReRoute specfic configuration. In the future we can add a feature that allows ReRoute specfic configuration.
In order to tell Ocelot a ReRoute is to use the service discovery provider for its host and port you must add the In order to tell Ocelot a ReRoute is to use the service discovery provider for its host and port you must add the ServiceName and load balancer you wish to use when making requests downstream. At the moment Ocelot has a RoundRobin and LeastConnection algorithm you can use. If no load balancer is specified Ocelot will not load balance requests.
ServiceName and load balancer you wish to use when making requests downstream. At the moment Ocelot has a RoundRobin
and LeastConnection algorithm you can use. If no load balancer is specified Ocelot will not load balance requests.
.. code-block:: json .. code-block:: json
@ -72,9 +68,7 @@ The polling interval is in milliseconds and tells Ocelot how often to call Consu
Please note there are tradeoffs here. If you poll Consul it is possible Ocelot will not know if a service is down depending on your polling interval and you might get more errors than if you get the latest services per request. This really depends on how volatile your services are. I doubt it will matter for most people and polling may give a tiny performance improvement over calling Consul per request (as sidecar agent). If you are calling a remote Consul agent then polling will be a good performance improvement. Please note there are tradeoffs here. If you poll Consul it is possible Ocelot will not know if a service is down depending on your polling interval and you might get more errors than if you get the latest services per request. This really depends on how volatile your services are. I doubt it will matter for most people and polling may give a tiny performance improvement over calling Consul per request (as sidecar agent). If you are calling a remote Consul agent then polling will be a good performance improvement.
Your services need to be added to Consul something like below (C# style but hopefully this make sense)...The only important thing to note Your services need to be added to Consul something like below (C# style but hopefully this make sense)...The only important thing to note is not to add http or https to the Address field. I have been contacted before about not accepting scheme in Address and accepting scheme in address. After reading `this <https://www.consul.io/docs/agent/services.html>`_ I don't think the scheme should be in there.
is not to add http or https to the Address field. I have been contacted before about not accepting scheme in Address and accepting scheme
in address. After reading `this <https://www.consul.io/docs/agent/services.html>`_ I don't think the scheme should be in there.
.. code-block: csharp .. code-block: csharp
@ -116,9 +110,7 @@ Ocelot will add this token to the Consul client that it uses to make requests an
Eureka Eureka
^^^^^^ ^^^^^^
This feature was requested as part of `Issue 262 <https://github.com/ThreeMammals/Ocelot/issues/262>`_ . to add support for Netflix's This feature was requested as part of `Issue 262 <https://github.com/ThreeMammals/Ocelot/issues/262>`_ . to add support for Netflix's Eureka service discovery provider. The main reason for this is it is a key part of `Steeltoe <https://steeltoe.io/>`_ which is something to do with `Pivotal <https://pivotal.io/platform>`_! Anyway enough of the background.
Eureka service discovery provider. The main reason for this is it is a key part of `Steeltoe <https://steeltoe.io/>`_ which is something
to do with `Pivotal <https://pivotal.io/platform>`_! Anyway enough of the background.
The first thing you need to do is install the NuGet package that provides Eureka support in Ocelot. The first thing you need to do is install the NuGet package that provides Eureka support in Ocelot.
@ -153,10 +145,7 @@ And following the guide `Here <https://steeltoe.io/docs/steeltoe-discovery/>`_ y
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. 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 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.
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.
Ocelot will use the scheme (http/https) set in Eureka if these values are not provided in ocelot.json Ocelot will use the scheme (http/https) set in Eureka if these values are not provided in ocelot.json

View File

@ -3,9 +3,7 @@ Service Fabric
If you have services deployed in Service Fabric you will normally use the naming service to access them. If you have services deployed in Service Fabric you will normally use the naming service to access them.
The following example shows how to set up a ReRoute that will work in Service Fabric. The most important thing is the ServiceName which is made up of the The following example shows how to set up a ReRoute that will work in Service Fabric. The most important thing is the ServiceName which is made up of the Service Fabric application name then the specific service name. We also need to set up the ServiceDiscoveryProvider in GlobalConfiguration. The example here shows a typical configuration. It assumes service fabric is running on localhost and that the naming service is on port 19081.
Service Fabric application name then the specific service name. We also need to set up the ServiceDiscoveryProvider in
GlobalConfiguration. The example here shows a typical configuration. It assumes service fabric is running on localhost and that the naming service is on port 19081.
The example below is taken from the samples folder so please check it if this doesnt make sense! The example below is taken from the samples folder so please check it if this doesnt make sense!

View File

@ -31,9 +31,7 @@ Then in your ocelot.json add the following to proxy a ReRoute using websockets.
], ],
} }
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 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.
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.
SignalR SignalR
^^^^^^^ ^^^^^^^
@ -77,9 +75,7 @@ Then in your ocelot.json add the following to proxy a ReRoute using SignalR. Not
] ]
} }
With this configuration set Ocelot will match any SignalR traffic that comes in on / and proxy it to localhost:5001/ws. To make this clearer With this configuration set Ocelot will match any SignalR 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.
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 Supported
^^^^^^^^^ ^^^^^^^^^
@ -88,8 +84,7 @@ Supported
2. Routing 2. Routing
3. Service Discovery 3. Service Discovery
This means that you can set up your downstream services running websockets and either have multiple DownstreamHostAndPorts in your ReRoute 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 :)
config or hook your ReRoute into a service discovery provider and then load balance requests...Which I think is pretty cool :)
Not Supported Not Supported
^^^^^^^^^^^^^ ^^^^^^^^^^^^^

View File

@ -1,23 +1,13 @@
Big Picture Big Picture
=========== ===========
Ocelot 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.
a micro services / service orientated architecture
that need a unified point of entry into their system.
In particular I want easy integration with In particular I want easy integration with IdentityServer reference and bearer tokens.
IdentityServer reference and bearer tokens.
Ocelot is a bunch of middlewares in a specific order. Ocelot is a bunch of middlewares in a specific order.
Ocelot manipulates the HttpRequest object into a state specified by its configuration until Ocelot manipulates the HttpRequest object into a state specified by its configuration until it reaches a request builder middleware where it creates a HttpRequestMessage object which is used to make a request to a downstream service. The middleware that makes the request is the last thing in the Ocelot pipeline. It does not call the next middleware. There is a piece of middleware that maps the HttpResponseMessage onto the HttpResponse object and that is returned to the client. That is basically it with a bunch of other features.
it reaches a request builder middleware where it creates a HttpRequestMessage object which is
used to make a request to a downstream service. The middleware that makes the request is
the last thing in the Ocelot pipeline. It does not call the next middleware.
The response from the downstream service is stored in a per request scoped repository
and retrieved as the requests goes back up the Ocelot pipeline. There is a piece of middleware
that maps the HttpResponseMessage onto the HttpResponse object and that is returned to the client.
That is basically it with a bunch of other features.
The following are configurations that you use when deploying Ocelot. The following are configurations that you use when deploying Ocelot.

View File

@ -1,5 +1,4 @@
Contributing Contributing
============ ============
Pull requests, issues and commentary welcome! No special process just create a request and get in Pull requests, issues and commentary welcome! No special process just create a request and get in touch either via gitter or create an issue.
touch either via gitter or create an issue.

View File

@ -52,16 +52,13 @@ If you want some example that actually does something use the following:
} }
} }
The most important thing to note here is BaseUrl. Ocelot needs to know the URL it is running under The most important thing to note here is BaseUrl. Ocelot needs to know the URL it is running under in order to do Header find & replace and for certain administration configurations. When setting this URL it should be the external URL that clients will see Ocelot running on e.g. If you are running containers Ocelot might run on the url http://123.12.1.1:6543 but has something like nginx in front of it responding on https://api.mybusiness.com. In this case the Ocelot base url should be https://api.mybusiness.com.
in order to do Header find & replace and for certain administration configurations. When setting this URL it should be the external URL that clients will see Ocelot running on e.g. If you are running containers Ocelot might run on the url http://123.12.1.1:6543 but has something like nginx in front of it responding on https://api.mybusiness.com. In this case the Ocelot base url should be https://api.mybusiness.com.
If you are using containers and require Ocelot to respond to clients on http://123.12.1.1:6543 If you are using containers and require Ocelot to respond to clients on http://123.12.1.1:6543 then you can do this, however if you are deploying multiple Ocelot's you will probably want to pass this on the command line in some kind of script. Hopefully whatever scheduler you are using can pass the IP.
then you can do this, however if you are deploying multiple Ocelot's you will probably want to pass this on the command line in some kind of script. Hopefully whatever scheduler you are using can pass the IP.
**Program** **Program**
Then in your Program.cs you will want to have the following. The main things to note are Then in your Program.cs you will want to have the following. The main things to note are AddOcelot() (adds ocelot services), UseOcelot().Wait() (sets up all the Ocelot middleware).
AddOcelot() (adds ocelot services), UseOcelot().Wait() (sets up all the Ocelot middleware).
.. code-block:: csharp .. code-block:: csharp
@ -116,8 +113,7 @@ AddOcelot() (adds ocelot services), UseOcelot().Wait() (sets up all the Ocelot m
**Install NuGet package** **Install NuGet package**
Install Ocelot and it's dependecies using nuget. You will need to create a netcoreapp1.0+ projct and bring the package into it. Then follow the Startup below and :doc:`../features/configuration` sections Install Ocelot and it's dependecies using nuget. You will need to create a netcoreapp1.0+ projct and bring the package into it. Then follow the Startup below and :doc:`../features/configuration` sections to get up and running. Please note you will need to choose one of the Ocelot packages from the NuGet feed.
to get up and running. Please note you will need to choose one of the Ocelot packages from the NuGet feed.
All versions can be found `here <https://www.nuget.org/packages/Ocelot/>`_. All versions can be found `here <https://www.nuget.org/packages/Ocelot/>`_.

View File

@ -7,10 +7,7 @@ Ocelot does not support...
* Forwarding a host header - The host header that you send to Ocelot will not be forwarded to the downstream service. Obviously this would break everything :( * Forwarding 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 ocelot.json but it doesnt fit into the vision * 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
I have for Ocelot. If you would like to have Swagger in Ocelot then you must roll your own swagger.json and do the following in your
Startup.cs or Program.cs. The code sample below registers a piece of middleware that loads your hand rolled swagger.json and returns
it on /swagger/v1/swagger.json. It then registers the SwaggerUI middleware from Swashbuckle.AspNetCore
.. code-block:: csharp .. code-block:: csharp
@ -28,16 +25,8 @@ Ocelot does not support...
app.UseOcelot().Wait(); app.UseOcelot().Wait();
The main reasons why I don't think Swagger makes sense is we already hand roll our definition in ocelot.json. 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.
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 their product service In addition to this many people will configure Ocelot to proxy all traffic like /products/{everything} to their 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.
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 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.
even be possible to write something that maps ocelot.json to the postman json spec. However I don't intend to do this.

11
helpers.txt Normal file
View File

@ -0,0 +1,11 @@
var downstreamReRoute = RequestScopedDataRepository.Get<DownstreamReRoute>("DownstreamReRoute");
// todo check downstreamReRoute is ok
var errors = RequestScopedDataRepository.Get<List<Error>>("Errors");
// todo check errors is ok
var downstreamResponse = RequestScopedDataRepository.Get<DownstreamResponse>("DownstreamResponse");
// todo check downstreamResponse is ok
var context = RequestScopedDataRepository.Get<DownstreamContext>("DownstreamContext").Data;
// todo check downstreamcontext is ok

View File

@ -1,11 +1,11 @@
namespace Ocelot.Provider.Consul namespace Ocelot.Provider.Consul
{ {
using Errors; using Ocelot.Errors;
public class UnableToSetConfigInConsulError : Error public class UnableToSetConfigInConsulError : Error
{ {
public UnableToSetConfigInConsulError(string s) public UnableToSetConfigInConsulError(string s)
: base(s, OcelotErrorCode.UnknownError) : base(s, OcelotErrorCode.UnknownError, 404)
{ {
} }
} }

View File

@ -1,12 +1,12 @@
namespace Ocelot.Provider.Polly namespace Ocelot.Provider.Polly
{ {
using Errors; using Ocelot.Errors;
using System; using System;
public class RequestTimedOutError : Error public class RequestTimedOutError : Error
{ {
public RequestTimedOutError(Exception exception) public RequestTimedOutError(Exception exception)
: base($"Timeout making http request, exception: {exception}", OcelotErrorCode.RequestTimedOutError) : base($"Timeout making http request, exception: {exception}", OcelotErrorCode.RequestTimedOutError, 503)
{ {
} }
} }

View File

@ -5,7 +5,7 @@
public class UnableToSaveAcceptCommand : Error public class UnableToSaveAcceptCommand : Error
{ {
public UnableToSaveAcceptCommand(string message) public UnableToSaveAcceptCommand(string message)
: base(message, OcelotErrorCode.UnknownError) : base(message, OcelotErrorCode.UnknownError, 404)
{ {
} }
} }

View File

@ -1,52 +1,56 @@
using Microsoft.AspNetCore.Authentication; namespace Ocelot.Authentication.Middleware
using Ocelot.Configuration;
using Ocelot.Logging;
using Ocelot.Middleware;
using System.Threading.Tasks;
namespace Ocelot.Authentication.Middleware
{ {
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Authentication;
using Ocelot.Configuration;
using Ocelot.Logging;
using Ocelot.Middleware;
using System.Threading.Tasks;
using Ocelot.DownstreamRouteFinder.Middleware;
public class AuthenticationMiddleware : OcelotMiddleware public class AuthenticationMiddleware : OcelotMiddleware
{ {
private readonly OcelotRequestDelegate _next; private readonly RequestDelegate _next;
public AuthenticationMiddleware(OcelotRequestDelegate next, public AuthenticationMiddleware(RequestDelegate next,
IOcelotLoggerFactory loggerFactory) IOcelotLoggerFactory loggerFactory)
: base(loggerFactory.CreateLogger<AuthenticationMiddleware>()) : base(loggerFactory.CreateLogger<AuthenticationMiddleware>())
{ {
_next = next; _next = next;
} }
public async Task Invoke(DownstreamContext context) public async Task Invoke(HttpContext httpContext)
{ {
if (context.HttpContext.Request.Method.ToUpper() != "OPTIONS" && IsAuthenticatedRoute(context.DownstreamReRoute)) var downstreamReRoute = httpContext.Items.DownstreamReRoute();
if (httpContext.Request.Method.ToUpper() != "OPTIONS" && IsAuthenticatedRoute(downstreamReRoute))
{ {
Logger.LogInformation($"{context.HttpContext.Request.Path} is an authenticated route. {MiddlewareName} checking if client is authenticated"); Logger.LogInformation($"{httpContext.Request.Path} is an authenticated route. {MiddlewareName} checking if client is authenticated");
var result = await context.HttpContext.AuthenticateAsync(context.DownstreamReRoute.AuthenticationOptions.AuthenticationProviderKey); var result = await httpContext.AuthenticateAsync(downstreamReRoute.AuthenticationOptions.AuthenticationProviderKey);
context.HttpContext.User = result.Principal; httpContext.User = result.Principal;
if (context.HttpContext.User.Identity.IsAuthenticated) if (httpContext.User.Identity.IsAuthenticated)
{ {
Logger.LogInformation($"Client has been authenticated for {context.HttpContext.Request.Path}"); Logger.LogInformation($"Client has been authenticated for {httpContext.Request.Path}");
await _next.Invoke(context); await _next.Invoke(httpContext);
} }
else else
{ {
var error = new UnauthenticatedError( var error = new UnauthenticatedError(
$"Request for authenticated route {context.HttpContext.Request.Path} by {context.HttpContext.User.Identity.Name} was unauthenticated"); $"Request for authenticated route {httpContext.Request.Path} by {httpContext.User.Identity.Name} was unauthenticated");
Logger.LogWarning($"Client has NOT been authenticated for {context.HttpContext.Request.Path} and pipeline error set. {error}"); Logger.LogWarning($"Client has NOT been authenticated for {httpContext.Request.Path} and pipeline error set. {error}");
SetPipelineError(context, error); httpContext.Items.SetError(error);
} }
} }
else else
{ {
Logger.LogInformation($"No authentication needed for {context.HttpContext.Request.Path}"); Logger.LogInformation($"No authentication needed for {httpContext.Request.Path}");
await _next.Invoke(context); await _next.Invoke(httpContext);
} }
} }

View File

@ -1,10 +1,10 @@
using Ocelot.Middleware.Pipeline;
namespace Ocelot.Authentication.Middleware namespace Ocelot.Authentication.Middleware
{ {
using Microsoft.AspNetCore.Builder;
public static class AuthenticationMiddlewareMiddlewareExtensions public static class AuthenticationMiddlewareMiddlewareExtensions
{ {
public static IOcelotPipelineBuilder UseAuthenticationMiddleware(this IOcelotPipelineBuilder builder) public static IApplicationBuilder UseAuthenticationMiddleware(this IApplicationBuilder builder)
{ {
return builder.UseMiddleware<AuthenticationMiddleware>(); return builder.UseMiddleware<AuthenticationMiddleware>();
} }

View File

@ -1,11 +1,12 @@
using Ocelot.Errors; namespace Ocelot.Authorisation
namespace Ocelot.Authorisation
{ {
using Ocelot.Errors;
using System.Net;
public class ClaimValueNotAuthorisedError : Error public class ClaimValueNotAuthorisedError : Error
{ {
public ClaimValueNotAuthorisedError(string message) public ClaimValueNotAuthorisedError(string message)
: base(message, OcelotErrorCode.ClaimValueNotAuthorisedError) : base(message, OcelotErrorCode.ClaimValueNotAuthorisedError, 403)
{ {
} }
} }

View File

@ -1,13 +1,12 @@
using Ocelot.DownstreamRouteFinder.UrlMatcher; namespace Ocelot.Authorisation
using Ocelot.Responses;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Text.RegularExpressions;
namespace Ocelot.Authorisation
{ {
using Infrastructure.Claims.Parser; using Ocelot.Infrastructure.Claims.Parser;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Responses;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Text.RegularExpressions;
public class ClaimsAuthoriser : IClaimsAuthoriser public class ClaimsAuthoriser : IClaimsAuthoriser
{ {

View File

@ -1,18 +1,20 @@
namespace Ocelot.Authorisation.Middleware namespace Ocelot.Authorisation.Middleware
{ {
using Configuration; using Ocelot.Configuration;
using Logging; using Ocelot.Logging;
using Ocelot.Middleware; using Ocelot.Middleware;
using Responses; using Ocelot.Responses;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Ocelot.DownstreamRouteFinder.Middleware;
public class AuthorisationMiddleware : OcelotMiddleware public class AuthorisationMiddleware : OcelotMiddleware
{ {
private readonly OcelotRequestDelegate _next; private readonly RequestDelegate _next;
private readonly IClaimsAuthoriser _claimsAuthoriser; private readonly IClaimsAuthoriser _claimsAuthoriser;
private readonly IScopesAuthoriser _scopesAuthoriser; private readonly IScopesAuthoriser _scopesAuthoriser;
public AuthorisationMiddleware(OcelotRequestDelegate next, public AuthorisationMiddleware(RequestDelegate next,
IClaimsAuthoriser claimsAuthoriser, IClaimsAuthoriser claimsAuthoriser,
IScopesAuthoriser scopesAuthoriser, IScopesAuthoriser scopesAuthoriser,
IOcelotLoggerFactory loggerFactory) IOcelotLoggerFactory loggerFactory)
@ -23,19 +25,21 @@
_scopesAuthoriser = scopesAuthoriser; _scopesAuthoriser = scopesAuthoriser;
} }
public async Task Invoke(DownstreamContext context) public async Task Invoke(HttpContext httpContext)
{ {
if (!IsOptionsHttpMethod(context) && IsAuthenticatedRoute(context.DownstreamReRoute)) var downstreamReRoute = httpContext.Items.DownstreamReRoute();
if (!IsOptionsHttpMethod(httpContext) && IsAuthenticatedRoute(downstreamReRoute))
{ {
Logger.LogInformation("route is authenticated scopes must be checked"); Logger.LogInformation("route is authenticated scopes must be checked");
var authorised = _scopesAuthoriser.Authorise(context.HttpContext.User, context.DownstreamReRoute.AuthenticationOptions.AllowedScopes); var authorised = _scopesAuthoriser.Authorise(httpContext.User, downstreamReRoute.AuthenticationOptions.AllowedScopes);
if (authorised.IsError) if (authorised.IsError)
{ {
Logger.LogWarning("error authorising user scopes"); Logger.LogWarning("error authorising user scopes");
SetPipelineError(context, authorised.Errors); httpContext.Items.UpsertErrors(authorised.Errors);
return; return;
} }
@ -47,41 +51,41 @@
{ {
Logger.LogWarning("user scopes is not authorised setting pipeline error"); Logger.LogWarning("user scopes is not authorised setting pipeline error");
SetPipelineError(context, new UnauthorisedError( httpContext.Items.SetError(new UnauthorisedError(
$"{context.HttpContext.User.Identity.Name} unable to access {context.DownstreamReRoute.UpstreamPathTemplate.OriginalValue}")); $"{httpContext.User.Identity.Name} unable to access {downstreamReRoute.UpstreamPathTemplate.OriginalValue}"));
} }
} }
if (!IsOptionsHttpMethod(context) && IsAuthorisedRoute(context.DownstreamReRoute)) if (!IsOptionsHttpMethod(httpContext) && IsAuthorisedRoute(downstreamReRoute))
{ {
Logger.LogInformation("route is authorised"); Logger.LogInformation("route is authorised");
var authorised = _claimsAuthoriser.Authorise(context.HttpContext.User, context.DownstreamReRoute.RouteClaimsRequirement, context.TemplatePlaceholderNameAndValues); var authorised = _claimsAuthoriser.Authorise(httpContext.User, downstreamReRoute.RouteClaimsRequirement, httpContext.Items.TemplatePlaceholderNameAndValues());
if (authorised.IsError) if (authorised.IsError)
{ {
Logger.LogWarning($"Error whilst authorising {context.HttpContext.User.Identity.Name}. Setting pipeline error"); Logger.LogWarning($"Error whilst authorising {httpContext.User.Identity.Name}. Setting pipeline error");
SetPipelineError(context, authorised.Errors); httpContext.Items.UpsertErrors(authorised.Errors);
return; return;
} }
if (IsAuthorised(authorised)) if (IsAuthorised(authorised))
{ {
Logger.LogInformation($"{context.HttpContext.User.Identity.Name} has succesfully been authorised for {context.DownstreamReRoute.UpstreamPathTemplate.OriginalValue}."); Logger.LogInformation($"{httpContext.User.Identity.Name} has succesfully been authorised for {downstreamReRoute.UpstreamPathTemplate.OriginalValue}.");
await _next.Invoke(context); await _next.Invoke(httpContext);
} }
else else
{ {
Logger.LogWarning($"{context.HttpContext.User.Identity.Name} is not authorised to access {context.DownstreamReRoute.UpstreamPathTemplate.OriginalValue}. Setting pipeline error"); Logger.LogWarning($"{httpContext.User.Identity.Name} is not authorised to access {downstreamReRoute.UpstreamPathTemplate.OriginalValue}. Setting pipeline error");
SetPipelineError(context, new UnauthorisedError($"{context.HttpContext.User.Identity.Name} is not authorised to access {context.DownstreamReRoute.UpstreamPathTemplate.OriginalValue}")); httpContext.Items.SetError(new UnauthorisedError($"{httpContext.User.Identity.Name} is not authorised to access {downstreamReRoute.UpstreamPathTemplate.OriginalValue}"));
} }
} }
else else
{ {
Logger.LogInformation($"{context.DownstreamReRoute.DownstreamPathTemplate.Value} route does not require user to be authorised"); Logger.LogInformation($"{downstreamReRoute.DownstreamPathTemplate.Value} route does not require user to be authorised");
await _next.Invoke(context); await _next.Invoke(httpContext);
} }
} }
@ -100,9 +104,9 @@
return reRoute.IsAuthorised; return reRoute.IsAuthorised;
} }
private static bool IsOptionsHttpMethod(DownstreamContext context) private static bool IsOptionsHttpMethod(HttpContext httpContext)
{ {
return context.HttpContext.Request.Method.ToUpper() == "OPTIONS"; return httpContext.Request.Method.ToUpper() == "OPTIONS";
} }
} }
} }

View File

@ -1,10 +1,10 @@
using Ocelot.Middleware.Pipeline;
namespace Ocelot.Authorisation.Middleware namespace Ocelot.Authorisation.Middleware
{ {
using Microsoft.AspNetCore.Builder;
public static class AuthorisationMiddlewareMiddlewareExtensions public static class AuthorisationMiddlewareMiddlewareExtensions
{ {
public static IOcelotPipelineBuilder UseAuthorisationMiddleware(this IOcelotPipelineBuilder builder) public static IApplicationBuilder UseAuthorisationMiddleware(this IApplicationBuilder builder)
{ {
return builder.UseMiddleware<AuthorisationMiddleware>(); return builder.UseMiddleware<AuthorisationMiddleware>();
} }

View File

@ -1,11 +1,11 @@
using Ocelot.Errors; namespace Ocelot.Authorisation
namespace Ocelot.Authorisation
{ {
using Ocelot.Errors;
public class ScopeNotAuthorisedError : Error public class ScopeNotAuthorisedError : Error
{ {
public ScopeNotAuthorisedError(string message) public ScopeNotAuthorisedError(string message)
: base(message, OcelotErrorCode.ScopeNotAuthorisedError) : base(message, OcelotErrorCode.ScopeNotAuthorisedError, 403)
{ {
} }
} }

View File

@ -1,11 +1,11 @@
using Ocelot.Errors; namespace Ocelot.Authorisation
namespace Ocelot.Authorisation
{ {
using Ocelot.Errors;
public class UnauthorisedError : Error public class UnauthorisedError : Error
{ {
public UnauthorisedError(string message) public UnauthorisedError(string message)
: base(message, OcelotErrorCode.UnauthorizedError) : base(message, OcelotErrorCode.UnauthorizedError, 403)
{ {
} }
} }

View File

@ -1,11 +1,11 @@
using Ocelot.Errors; namespace Ocelot.Authorisation
namespace Ocelot.Authorisation
{ {
using Ocelot.Errors;
public class UserDoesNotHaveClaimError : Error public class UserDoesNotHaveClaimError : Error
{ {
public UserDoesNotHaveClaimError(string message) public UserDoesNotHaveClaimError(string message)
: base(message, OcelotErrorCode.UserDoesNotHaveClaimError) : base(message, OcelotErrorCode.UserDoesNotHaveClaimError, 403)
{ {
} }
} }

View File

@ -1,18 +1,18 @@
using Ocelot.Middleware; namespace Ocelot.Cache
using System.Text;
using System.Threading.Tasks;
namespace Ocelot.Cache
{ {
using Ocelot.Request.Middleware;
using System.Text;
using System.Threading.Tasks;
public class CacheKeyGenerator : ICacheKeyGenerator public class CacheKeyGenerator : ICacheKeyGenerator
{ {
public string GenerateRequestCacheKey(DownstreamContext context) public string GenerateRequestCacheKey(DownstreamRequest downstreamRequest)
{ {
string hashedContent = null; string hashedContent = null;
StringBuilder downStreamUrlKeyBuilder = new StringBuilder($"{context.DownstreamRequest.Method}-{context.DownstreamRequest.OriginalString}"); StringBuilder downStreamUrlKeyBuilder = new StringBuilder($"{downstreamRequest.Method}-{downstreamRequest.OriginalString}");
if (context.DownstreamRequest.Content != null) if (downstreamRequest.Content != null)
{ {
string requestContentString = Task.Run(async () => await context.DownstreamRequest.Content.ReadAsStringAsync()).Result; string requestContentString = Task.Run(async () => await downstreamRequest.Content.ReadAsStringAsync()).Result;
downStreamUrlKeyBuilder.Append(requestContentString); downStreamUrlKeyBuilder.Append(requestContentString);
} }

View File

@ -1,9 +1,9 @@
using Ocelot.Middleware; namespace Ocelot.Cache
namespace Ocelot.Cache
{ {
using Ocelot.Request.Middleware;
public interface ICacheKeyGenerator public interface ICacheKeyGenerator
{ {
string GenerateRequestCacheKey(DownstreamContext context); string GenerateRequestCacheKey(DownstreamRequest downstreamRequest);
} }
} }

View File

@ -6,47 +6,52 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Ocelot.DownstreamRouteFinder.Middleware;
public class OutputCacheMiddleware : OcelotMiddleware public class OutputCacheMiddleware : OcelotMiddleware
{ {
private readonly OcelotRequestDelegate _next; private readonly RequestDelegate _next;
private readonly IOcelotCache<CachedResponse> _outputCache; private readonly IOcelotCache<CachedResponse> _outputCache;
private readonly ICacheKeyGenerator _cacheGeneratot; private readonly ICacheKeyGenerator _cacheGenerator;
public OutputCacheMiddleware(OcelotRequestDelegate next, public OutputCacheMiddleware(RequestDelegate next,
IOcelotLoggerFactory loggerFactory, IOcelotLoggerFactory loggerFactory,
IOcelotCache<CachedResponse> outputCache, IOcelotCache<CachedResponse> outputCache,
ICacheKeyGenerator cacheGeneratot) ICacheKeyGenerator cacheGenerator)
: base(loggerFactory.CreateLogger<OutputCacheMiddleware>()) : base(loggerFactory.CreateLogger<OutputCacheMiddleware>())
{ {
_next = next; _next = next;
_outputCache = outputCache; _outputCache = outputCache;
_cacheGeneratot = cacheGeneratot; _cacheGenerator = cacheGenerator;
} }
public async Task Invoke(DownstreamContext context) public async Task Invoke(HttpContext httpContext)
{ {
if (!context.DownstreamReRoute.IsCached) var downstreamReRoute = httpContext.Items.DownstreamReRoute();
if (!downstreamReRoute.IsCached)
{ {
await _next.Invoke(context); await _next.Invoke(httpContext);
return; return;
} }
var downstreamUrlKey = $"{context.DownstreamRequest.Method}-{context.DownstreamRequest.OriginalString}"; var downstreamRequest = httpContext.Items.DownstreamRequest();
string downStreamRequestCacheKey = _cacheGeneratot.GenerateRequestCacheKey(context);
var downstreamUrlKey = $"{downstreamRequest.Method}-{downstreamRequest.OriginalString}";
string downStreamRequestCacheKey = _cacheGenerator.GenerateRequestCacheKey(downstreamRequest);
Logger.LogDebug($"Started checking cache for {downstreamUrlKey}"); Logger.LogDebug($"Started checking cache for {downstreamUrlKey}");
var cached = _outputCache.Get(downStreamRequestCacheKey, context.DownstreamReRoute.CacheOptions.Region); var cached = _outputCache.Get(downStreamRequestCacheKey, downstreamReRoute.CacheOptions.Region);
if (cached != null) if (cached != null)
{ {
Logger.LogDebug($"cache entry exists for {downstreamUrlKey}"); Logger.LogDebug($"cache entry exists for {downstreamUrlKey}");
var response = CreateHttpResponseMessage(cached); var response = CreateHttpResponseMessage(cached);
SetHttpResponseMessageThisRequest(context, response); SetHttpResponseMessageThisRequest(httpContext, response);
Logger.LogDebug($"finished returned cached response for {downstreamUrlKey}"); Logger.LogDebug($"finished returned cached response for {downstreamUrlKey}");
@ -55,26 +60,28 @@
Logger.LogDebug($"no resonse cached for {downstreamUrlKey}"); Logger.LogDebug($"no resonse cached for {downstreamUrlKey}");
await _next.Invoke(context); await _next.Invoke(httpContext);
if (context.IsError) if (httpContext.Items.Errors().Count > 0)
{ {
Logger.LogDebug($"there was a pipeline error for {downstreamUrlKey}"); Logger.LogDebug($"there was a pipeline error for {downstreamUrlKey}");
return; return;
} }
cached = await CreateCachedResponse(context.DownstreamResponse); var downstreamResponse = httpContext.Items.DownstreamResponse();
_outputCache.Add(downStreamRequestCacheKey, cached, TimeSpan.FromSeconds(context.DownstreamReRoute.CacheOptions.TtlSeconds), context.DownstreamReRoute.CacheOptions.Region); cached = await CreateCachedResponse(downstreamResponse);
_outputCache.Add(downStreamRequestCacheKey, cached, TimeSpan.FromSeconds(downstreamReRoute.CacheOptions.TtlSeconds), downstreamReRoute.CacheOptions.Region);
Logger.LogDebug($"finished response added to cache for {downstreamUrlKey}"); Logger.LogDebug($"finished response added to cache for {downstreamUrlKey}");
} }
private void SetHttpResponseMessageThisRequest(DownstreamContext context, private void SetHttpResponseMessageThisRequest(HttpContext context,
DownstreamResponse response) DownstreamResponse response)
{ {
context.DownstreamResponse = response; context.Items.UpsertDownstreamResponse(response);
} }
internal DownstreamResponse CreateHttpResponseMessage(CachedResponse cached) internal DownstreamResponse CreateHttpResponseMessage(CachedResponse cached)

View File

@ -1,11 +1,10 @@
using Microsoft.AspNetCore.Builder; namespace Ocelot.Cache.Middleware
using Ocelot.Middleware.Pipeline;
namespace Ocelot.Cache.Middleware
{ {
using Microsoft.AspNetCore.Builder;
public static class OutputCacheMiddlewareExtensions public static class OutputCacheMiddlewareExtensions
{ {
public static IOcelotPipelineBuilder UseOutputCacheMiddleware(this IOcelotPipelineBuilder builder) public static IApplicationBuilder UseOutputCacheMiddleware(this IApplicationBuilder builder)
{ {
return builder.UseMiddleware<OutputCacheMiddleware>(); return builder.UseMiddleware<OutputCacheMiddleware>();
} }

View File

@ -1,10 +1,10 @@
using Ocelot.Middleware.Pipeline; namespace Ocelot.Claims.Middleware
namespace Ocelot.Claims.Middleware
{ {
using Microsoft.AspNetCore.Builder;
public static class ClaimsBuilderMiddlewareExtensions public static class ClaimsBuilderMiddlewareExtensions
{ {
public static IOcelotPipelineBuilder UseClaimsToClaimsMiddleware(this IOcelotPipelineBuilder builder) public static IApplicationBuilder UseClaimsToClaimsMiddleware(this IApplicationBuilder builder)
{ {
return builder.UseMiddleware<ClaimsToClaimsMiddleware>(); return builder.UseMiddleware<ClaimsToClaimsMiddleware>();
} }

View File

@ -1,16 +1,18 @@
using Ocelot.Logging; namespace Ocelot.Claims.Middleware
using Ocelot.Middleware;
using System.Linq;
using System.Threading.Tasks;
namespace Ocelot.Claims.Middleware
{ {
using Microsoft.AspNetCore.Http;
using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.Logging;
using Ocelot.Middleware;
using System.Linq;
using System.Threading.Tasks;
public class ClaimsToClaimsMiddleware : OcelotMiddleware public class ClaimsToClaimsMiddleware : OcelotMiddleware
{ {
private readonly OcelotRequestDelegate _next; private readonly RequestDelegate _next;
private readonly IAddClaimsToRequest _addClaimsToRequest; private readonly IAddClaimsToRequest _addClaimsToRequest;
public ClaimsToClaimsMiddleware(OcelotRequestDelegate next, public ClaimsToClaimsMiddleware(RequestDelegate next,
IOcelotLoggerFactory loggerFactory, IOcelotLoggerFactory loggerFactory,
IAddClaimsToRequest addClaimsToRequest) IAddClaimsToRequest addClaimsToRequest)
: base(loggerFactory.CreateLogger<ClaimsToClaimsMiddleware>()) : base(loggerFactory.CreateLogger<ClaimsToClaimsMiddleware>())
@ -19,24 +21,26 @@ namespace Ocelot.Claims.Middleware
_addClaimsToRequest = addClaimsToRequest; _addClaimsToRequest = addClaimsToRequest;
} }
public async Task Invoke(DownstreamContext context) public async Task Invoke(HttpContext httpContext)
{ {
if (context.DownstreamReRoute.ClaimsToClaims.Any()) var downstreamReRoute = httpContext.Items.DownstreamReRoute();
if (downstreamReRoute.ClaimsToClaims.Any())
{ {
Logger.LogDebug("this route has instructions to convert claims to other claims"); Logger.LogDebug("this route has instructions to convert claims to other claims");
var result = _addClaimsToRequest.SetClaimsOnContext(context.DownstreamReRoute.ClaimsToClaims, context.HttpContext); var result = _addClaimsToRequest.SetClaimsOnContext(downstreamReRoute.ClaimsToClaims, httpContext);
if (result.IsError) if (result.IsError)
{ {
Logger.LogDebug("error converting claims to other claims, setting pipeline error"); Logger.LogDebug("error converting claims to other claims, setting pipeline error");
SetPipelineError(context, result.Errors); httpContext.Items.UpsertErrors(result.Errors);
return; return;
} }
} }
await _next.Invoke(context); await _next.Invoke(httpContext);
} }
} }
} }

View File

@ -5,7 +5,7 @@ namespace Ocelot.Configuration.Parser
public class InstructionNotForClaimsError : Error public class InstructionNotForClaimsError : Error
{ {
public InstructionNotForClaimsError() public InstructionNotForClaimsError()
: base("instructions did not contain claims, at the moment we only support claims extraction", OcelotErrorCode.InstructionNotForClaimsError) : base("instructions did not contain claims, at the moment we only support claims extraction", OcelotErrorCode.InstructionNotForClaimsError, 404)
{ {
} }
} }

View File

@ -5,7 +5,7 @@ namespace Ocelot.Configuration.Parser
public class NoInstructionsError : Error public class NoInstructionsError : Error
{ {
public NoInstructionsError(string splitToken) public NoInstructionsError(string splitToken)
: base($"There we no instructions splitting on {splitToken}", OcelotErrorCode.NoInstructionsError) : base($"There we no instructions splitting on {splitToken}", OcelotErrorCode.NoInstructionsError, 404)
{ {
} }
} }

View File

@ -6,7 +6,7 @@ namespace Ocelot.Configuration.Parser
public class ParsingConfigurationHeaderError : Error public class ParsingConfigurationHeaderError : Error
{ {
public ParsingConfigurationHeaderError(Exception exception) public ParsingConfigurationHeaderError(Exception exception)
: base($"error parsing configuration eception is {exception.Message}", OcelotErrorCode.ParsingConfigurationHeaderError) : base($"error parsing configuration eception is {exception.Message}", OcelotErrorCode.ParsingConfigurationHeaderError, 404)
{ {
} }
} }

View File

@ -5,7 +5,7 @@
public class FileValidationFailedError : Error public class FileValidationFailedError : Error
{ {
public FileValidationFailedError(string message) public FileValidationFailedError(string message)
: base(message, OcelotErrorCode.FileValidationFailedError) : base(message, OcelotErrorCode.FileValidationFailedError, 404)
{ {
} }
} }

View File

@ -1,6 +1,6 @@
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Ocelot.Middleware.Multiplexer; using Ocelot.Multiplexer;
using System; using System;
using System.Net.Http; using System.Net.Http;
using Ocelot.Configuration; using Ocelot.Configuration;

View File

@ -27,7 +27,7 @@ namespace Ocelot.DependencyInjection
using Ocelot.LoadBalancer.LoadBalancers; using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.Middleware; using Ocelot.Middleware;
using Ocelot.Middleware.Multiplexer; using Ocelot.Multiplexer;
using Ocelot.PathManipulation; using Ocelot.PathManipulation;
using Ocelot.QueryStrings; using Ocelot.QueryStrings;
using Ocelot.RateLimit; using Ocelot.RateLimit;
@ -128,7 +128,6 @@ namespace Ocelot.DependencyInjection
Services.TryAddSingleton<IRequestScopedDataRepository, HttpDataRepository>(); Services.TryAddSingleton<IRequestScopedDataRepository, HttpDataRepository>();
Services.AddMemoryCache(); Services.AddMemoryCache();
Services.TryAddSingleton<OcelotDiagnosticListener>(); Services.TryAddSingleton<OcelotDiagnosticListener>();
Services.TryAddSingleton<IMultiplexer, Multiplexer>();
Services.TryAddSingleton<IResponseAggregator, SimpleJsonResponseAggregator>(); Services.TryAddSingleton<IResponseAggregator, SimpleJsonResponseAggregator>();
Services.TryAddSingleton<ITracingHandlerFactory, TracingHandlerFactory>(); Services.TryAddSingleton<ITracingHandlerFactory, TracingHandlerFactory>();
Services.TryAddSingleton<IFileConfigurationPollerOptions, InMemoryFileConfigurationPollerOptions>(); Services.TryAddSingleton<IFileConfigurationPollerOptions, InMemoryFileConfigurationPollerOptions>();

View File

@ -1,16 +1,19 @@
using Ocelot.Logging; namespace Ocelot.DownstreamPathManipulation.Middleware
using Ocelot.Middleware;
using System.Linq;
using System.Threading.Tasks;
namespace Ocelot.PathManipulation.Middleware
{ {
using System.Linq;
using System.Threading.Tasks;
using Ocelot.Logging;
using Microsoft.AspNetCore.Http;
using Ocelot.Middleware;
using Ocelot.PathManipulation;
using Ocelot.DownstreamRouteFinder.Middleware;
public class ClaimsToDownstreamPathMiddleware : OcelotMiddleware public class ClaimsToDownstreamPathMiddleware : OcelotMiddleware
{ {
private readonly OcelotRequestDelegate _next; private readonly RequestDelegate _next;
private readonly IChangeDownstreamPathTemplate _changeDownstreamPathTemplate; private readonly IChangeDownstreamPathTemplate _changeDownstreamPathTemplate;
public ClaimsToDownstreamPathMiddleware(OcelotRequestDelegate next, public ClaimsToDownstreamPathMiddleware(RequestDelegate next,
IOcelotLoggerFactory loggerFactory, IOcelotLoggerFactory loggerFactory,
IChangeDownstreamPathTemplate changeDownstreamPathTemplate) IChangeDownstreamPathTemplate changeDownstreamPathTemplate)
: base(loggerFactory.CreateLogger<ClaimsToDownstreamPathMiddleware>()) : base(loggerFactory.CreateLogger<ClaimsToDownstreamPathMiddleware>())
@ -19,24 +22,29 @@ namespace Ocelot.PathManipulation.Middleware
_changeDownstreamPathTemplate = changeDownstreamPathTemplate; _changeDownstreamPathTemplate = changeDownstreamPathTemplate;
} }
public async Task Invoke(DownstreamContext context) public async Task Invoke(HttpContext httpContext)
{ {
if (context.DownstreamReRoute.ClaimsToPath.Any()) var downstreamReRoute = httpContext.Items.DownstreamReRoute();
if (downstreamReRoute.ClaimsToPath.Any())
{ {
Logger.LogInformation($"{context.DownstreamReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to path"); Logger.LogInformation($"{downstreamReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to path");
var response = _changeDownstreamPathTemplate.ChangeDownstreamPath(context.DownstreamReRoute.ClaimsToPath, context.HttpContext.User.Claims,
context.DownstreamReRoute.DownstreamPathTemplate, context.TemplatePlaceholderNameAndValues); var templatePlaceholderNameAndValues = httpContext.Items.TemplatePlaceholderNameAndValues();
var response = _changeDownstreamPathTemplate.ChangeDownstreamPath(downstreamReRoute.ClaimsToPath, httpContext.User.Claims,
downstreamReRoute.DownstreamPathTemplate, templatePlaceholderNameAndValues);
if (response.IsError) if (response.IsError)
{ {
Logger.LogWarning("there was an error setting queries on context, setting pipeline error"); Logger.LogWarning("there was an error setting queries on context, setting pipeline error");
SetPipelineError(context, response.Errors); httpContext.Items.UpsertErrors(response.Errors);
return; return;
} }
} }
await _next.Invoke(context); await _next.Invoke(httpContext);
} }
} }
} }

View File

@ -1,10 +1,10 @@
using Ocelot.Middleware.Pipeline; namespace Ocelot.DownstreamPathManipulation.Middleware
namespace Ocelot.PathManipulation.Middleware
{ {
using Microsoft.AspNetCore.Builder;
public static class ClaimsToDownstreamPathMiddlewareExtensions public static class ClaimsToDownstreamPathMiddlewareExtensions
{ {
public static IOcelotPipelineBuilder UseClaimsToDownstreamPathMiddleware(this IOcelotPipelineBuilder builder) public static IApplicationBuilder UseClaimsToDownstreamPathMiddleware(this IApplicationBuilder builder)
{ {
return builder.UseMiddleware<ClaimsToDownstreamPathMiddleware>(); return builder.UseMiddleware<ClaimsToDownstreamPathMiddleware>();
} }

View File

@ -1,11 +1,15 @@
using Ocelot.Configuration; namespace Ocelot.DownstreamRouteFinder
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using System.Collections.Generic;
namespace Ocelot.DownstreamRouteFinder
{ {
using Ocelot.Configuration;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using System.Collections.Generic;
public class DownstreamRoute public class DownstreamRoute
{ {
public DownstreamRoute()
{
}
public DownstreamRoute(List<PlaceholderNameAndValue> templatePlaceholderNameAndValues, ReRoute reRoute) public DownstreamRoute(List<PlaceholderNameAndValue> templatePlaceholderNameAndValues, ReRoute reRoute)
{ {
TemplatePlaceholderNameAndValues = templatePlaceholderNameAndValues; TemplatePlaceholderNameAndValues = templatePlaceholderNameAndValues;

View File

@ -5,7 +5,7 @@ namespace Ocelot.DownstreamRouteFinder.Finder
public class UnableToFindDownstreamRouteError : Error public class UnableToFindDownstreamRouteError : Error
{ {
public UnableToFindDownstreamRouteError(string path, string httpVerb) public UnableToFindDownstreamRouteError(string path, string httpVerb)
: base($"Failed to match ReRoute configuration for upstream path: {path}, verb: {httpVerb}.", OcelotErrorCode.UnableToFindDownstreamRouteError) : base($"Failed to match ReRoute configuration for upstream path: {path}, verb: {httpVerb}.", OcelotErrorCode.UnableToFindDownstreamRouteError, 404)
{ {
} }
} }

View File

@ -1,59 +1,61 @@
using Ocelot.DownstreamRouteFinder.Finder;
using Ocelot.Infrastructure.Extensions;
using Ocelot.Logging;
using Ocelot.Middleware;
using Ocelot.Middleware.Multiplexer;
using System.Linq;
using System.Threading.Tasks;
namespace Ocelot.DownstreamRouteFinder.Middleware namespace Ocelot.DownstreamRouteFinder.Middleware
{ {
using Microsoft.AspNetCore.Http;
using Ocelot.DownstreamRouteFinder.Finder;
using Ocelot.Infrastructure.Extensions;
using Ocelot.Logging;
using Ocelot.Middleware;
using System.Linq;
using System.Threading.Tasks;
public class DownstreamRouteFinderMiddleware : OcelotMiddleware public class DownstreamRouteFinderMiddleware : OcelotMiddleware
{ {
private readonly OcelotRequestDelegate _next; private readonly RequestDelegate _next;
private readonly IDownstreamRouteProviderFactory _factory; private readonly IDownstreamRouteProviderFactory _factory;
private readonly IMultiplexer _multiplexer;
public DownstreamRouteFinderMiddleware(OcelotRequestDelegate next, public DownstreamRouteFinderMiddleware(RequestDelegate next,
IOcelotLoggerFactory loggerFactory, IOcelotLoggerFactory loggerFactory,
IDownstreamRouteProviderFactory downstreamRouteFinder, IDownstreamRouteProviderFactory downstreamRouteFinder
IMultiplexer multiplexer) )
: base(loggerFactory.CreateLogger<DownstreamRouteFinderMiddleware>()) : base(loggerFactory.CreateLogger<DownstreamRouteFinderMiddleware>())
{ {
_multiplexer = multiplexer;
_next = next; _next = next;
_factory = downstreamRouteFinder; _factory = downstreamRouteFinder;
} }
public async Task Invoke(DownstreamContext context) public async Task Invoke(HttpContext httpContext)
{ {
var upstreamUrlPath = context.HttpContext.Request.Path.ToString(); var upstreamUrlPath = httpContext.Request.Path.ToString();
var upstreamQueryString = context.HttpContext.Request.QueryString.ToString(); var upstreamQueryString = httpContext.Request.QueryString.ToString();
var upstreamHost = context.HttpContext.Request.Headers["Host"]; var upstreamHost = httpContext.Request.Headers["Host"];
Logger.LogDebug($"Upstream url path is {upstreamUrlPath}"); Logger.LogDebug($"Upstream url path is {upstreamUrlPath}");
var provider = _factory.Get(context.Configuration); var internalConfiguration = httpContext.Items.IInternalConfiguration();
var downstreamRoute = provider.Get(upstreamUrlPath, upstreamQueryString, context.HttpContext.Request.Method, context.Configuration, upstreamHost); var provider = _factory.Get(internalConfiguration);
if (downstreamRoute.IsError) var response = provider.Get(upstreamUrlPath, upstreamQueryString, httpContext.Request.Method, internalConfiguration, upstreamHost);
if (response.IsError)
{ {
Logger.LogWarning($"{MiddlewareName} setting pipeline errors. IDownstreamRouteFinder returned {downstreamRoute.Errors.ToErrorString()}"); Logger.LogWarning($"{MiddlewareName} setting pipeline errors. IDownstreamRouteFinder returned {response.Errors.ToErrorString()}");
SetPipelineError(context, downstreamRoute.Errors); httpContext.Items.UpsertErrors(response.Errors);
return; return;
} }
var downstreamPathTemplates = string.Join(", ", downstreamRoute.Data.ReRoute.DownstreamReRoute.Select(r => r.DownstreamPathTemplate.Value)); var downstreamPathTemplates = string.Join(", ", response.Data.ReRoute.DownstreamReRoute.Select(r => r.DownstreamPathTemplate.Value));
Logger.LogDebug($"downstream templates are {downstreamPathTemplates}"); Logger.LogDebug($"downstream templates are {downstreamPathTemplates}");
context.TemplatePlaceholderNameAndValues = downstreamRoute.Data.TemplatePlaceholderNameAndValues; // why set both of these on HttpContext
httpContext.Items.UpsertTemplatePlaceholderNameAndValues(response.Data.TemplatePlaceholderNameAndValues);
await _multiplexer.Multiplex(context, downstreamRoute.Data.ReRoute, _next); httpContext.Items.UpsertDownstreamRoute(response.Data);
await _next.Invoke(httpContext);
} }
} }
} }

View File

@ -1,11 +1,10 @@
using Microsoft.AspNetCore.Builder;
using Ocelot.Middleware.Pipeline;
namespace Ocelot.DownstreamRouteFinder.Middleware namespace Ocelot.DownstreamRouteFinder.Middleware
{ {
using Microsoft.AspNetCore.Builder;
public static class DownstreamRouteFinderMiddlewareExtensions public static class DownstreamRouteFinderMiddlewareExtensions
{ {
public static IOcelotPipelineBuilder UseDownstreamRouteFinderMiddleware(this IOcelotPipelineBuilder builder) public static IApplicationBuilder UseDownstreamRouteFinderMiddleware(this IApplicationBuilder builder)
{ {
return builder.UseMiddleware<DownstreamRouteFinderMiddleware>(); return builder.UseMiddleware<DownstreamRouteFinderMiddleware>();
} }

View File

@ -1,52 +1,69 @@
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
using Ocelot.Logging;
using Ocelot.Middleware;
using Ocelot.Responses;
using Ocelot.Values;
using System;
using System.Threading.Tasks;
namespace Ocelot.DownstreamUrlCreator.Middleware namespace Ocelot.DownstreamUrlCreator.Middleware
{ {
using System.Collections.Generic;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Ocelot.Configuration;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Microsoft.AspNetCore.Http;
using Ocelot.Request.Middleware;
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
using Ocelot.Logging;
using Ocelot.Middleware;
using Ocelot.Responses;
using Ocelot.Values;
using System;
using System.Threading.Tasks;
using Ocelot.DownstreamRouteFinder.Middleware;
public class DownstreamUrlCreatorMiddleware : OcelotMiddleware public class DownstreamUrlCreatorMiddleware : OcelotMiddleware
{ {
private readonly OcelotRequestDelegate _next; private readonly RequestDelegate _next;
private readonly IDownstreamPathPlaceholderReplacer _replacer; private readonly IDownstreamPathPlaceholderReplacer _replacer;
public DownstreamUrlCreatorMiddleware(OcelotRequestDelegate next, public DownstreamUrlCreatorMiddleware(RequestDelegate next,
IOcelotLoggerFactory loggerFactory, IOcelotLoggerFactory loggerFactory,
IDownstreamPathPlaceholderReplacer replacer) IDownstreamPathPlaceholderReplacer replacer
)
: base(loggerFactory.CreateLogger<DownstreamUrlCreatorMiddleware>()) : base(loggerFactory.CreateLogger<DownstreamUrlCreatorMiddleware>())
{ {
_next = next; _next = next;
_replacer = replacer; _replacer = replacer;
} }
public async Task Invoke(DownstreamContext context) public async Task Invoke(HttpContext httpContext)
{ {
var downstreamReRoute = httpContext.Items.DownstreamReRoute();
var templatePlaceholderNameAndValues = httpContext.Items.TemplatePlaceholderNameAndValues();
var response = _replacer var response = _replacer
.Replace(context.DownstreamReRoute.DownstreamPathTemplate.Value, context.TemplatePlaceholderNameAndValues); .Replace(downstreamReRoute.DownstreamPathTemplate.Value, templatePlaceholderNameAndValues);
var downstreamRequest = httpContext.Items.DownstreamRequest();
if (response.IsError) if (response.IsError)
{ {
Logger.LogDebug("IDownstreamPathPlaceholderReplacer returned an error, setting pipeline error"); Logger.LogDebug("IDownstreamPathPlaceholderReplacer returned an error, setting pipeline error");
SetPipelineError(context, response.Errors); httpContext.Items.UpsertErrors(response.Errors);
return; return;
} }
if (!string.IsNullOrEmpty(context.DownstreamReRoute.DownstreamScheme)) if (!string.IsNullOrEmpty(downstreamReRoute.DownstreamScheme))
{ {
context.DownstreamRequest.Scheme = context.DownstreamReRoute.DownstreamScheme; //todo make sure this works, hopefully there is a test ;E
httpContext.Items.DownstreamRequest().Scheme = downstreamReRoute.DownstreamScheme;
} }
if (ServiceFabricRequest(context)) var internalConfiguration = httpContext.Items.IInternalConfiguration();
if (ServiceFabricRequest(internalConfiguration, downstreamReRoute))
{ {
var pathAndQuery = CreateServiceFabricUri(context, response); var pathAndQuery = CreateServiceFabricUri(downstreamRequest, downstreamReRoute, templatePlaceholderNameAndValues, response);
context.DownstreamRequest.AbsolutePath = pathAndQuery.path;
context.DownstreamRequest.Query = pathAndQuery.query; //todo check this works again hope there is a test..
downstreamRequest.AbsolutePath = pathAndQuery.path;
downstreamRequest.Query = pathAndQuery.query;
} }
else else
{ {
@ -54,48 +71,48 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
if (ContainsQueryString(dsPath)) if (ContainsQueryString(dsPath))
{ {
context.DownstreamRequest.AbsolutePath = GetPath(dsPath); downstreamRequest.AbsolutePath = GetPath(dsPath);
if (string.IsNullOrEmpty(context.DownstreamRequest.Query)) if (string.IsNullOrEmpty(downstreamRequest.Query))
{ {
context.DownstreamRequest.Query = GetQueryString(dsPath); downstreamRequest.Query = GetQueryString(dsPath);
} }
else else
{ {
context.DownstreamRequest.Query += GetQueryString(dsPath).Replace('?', '&'); downstreamRequest.Query += GetQueryString(dsPath).Replace('?', '&');
} }
} }
else else
{ {
RemoveQueryStringParametersThatHaveBeenUsedInTemplate(context); RemoveQueryStringParametersThatHaveBeenUsedInTemplate(downstreamRequest, templatePlaceholderNameAndValues);
context.DownstreamRequest.AbsolutePath = dsPath.Value; downstreamRequest.AbsolutePath = dsPath.Value;
} }
} }
Logger.LogDebug($"Downstream url is {context.DownstreamRequest}"); Logger.LogDebug($"Downstream url is {downstreamRequest}");
await _next.Invoke(context); await _next.Invoke(httpContext);
} }
private static void RemoveQueryStringParametersThatHaveBeenUsedInTemplate(DownstreamContext context) private static void RemoveQueryStringParametersThatHaveBeenUsedInTemplate(DownstreamRequest downstreamRequest, List<PlaceholderNameAndValue> templatePlaceholderNameAndValues)
{ {
foreach (var nAndV in context.TemplatePlaceholderNameAndValues) foreach (var nAndV in templatePlaceholderNameAndValues)
{ {
var name = nAndV.Name.Replace("{", "").Replace("}", ""); var name = nAndV.Name.Replace("{", "").Replace("}", "");
if (context.DownstreamRequest.Query.Contains(name) && if (downstreamRequest.Query.Contains(name) &&
context.DownstreamRequest.Query.Contains(nAndV.Value)) downstreamRequest.Query.Contains(nAndV.Value))
{ {
var questionMarkOrAmpersand = context.DownstreamRequest.Query.IndexOf(name, StringComparison.Ordinal); var questionMarkOrAmpersand = downstreamRequest.Query.IndexOf(name, StringComparison.Ordinal);
context.DownstreamRequest.Query = context.DownstreamRequest.Query.Remove(questionMarkOrAmpersand - 1, 1); downstreamRequest.Query = downstreamRequest.Query.Remove(questionMarkOrAmpersand - 1, 1);
var rgx = new Regex($@"\b{name}={nAndV.Value}\b"); var rgx = new Regex($@"\b{name}={nAndV.Value}\b");
context.DownstreamRequest.Query = rgx.Replace(context.DownstreamRequest.Query, ""); downstreamRequest.Query = rgx.Replace(downstreamRequest.Query, "");
if (!string.IsNullOrEmpty(context.DownstreamRequest.Query)) if (!string.IsNullOrEmpty(downstreamRequest.Query))
{ {
context.DownstreamRequest.Query = '?' + context.DownstreamRequest.Query.Substring(1); downstreamRequest.Query = '?' + downstreamRequest.Query.Substring(1);
} }
} }
} }
@ -116,17 +133,17 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
return dsPath.Value.Contains("?"); return dsPath.Value.Contains("?");
} }
private (string path, string query) CreateServiceFabricUri(DownstreamContext context, Response<DownstreamPath> dsPath) private (string path, string query) CreateServiceFabricUri(DownstreamRequest downstreamRequest, DownstreamReRoute downstreamReRoute, List<PlaceholderNameAndValue> templatePlaceholderNameAndValues, Response<DownstreamPath> dsPath)
{ {
var query = context.DownstreamRequest.Query; var query = downstreamRequest.Query;
var serviceName = _replacer.Replace(context.DownstreamReRoute.ServiceName, context.TemplatePlaceholderNameAndValues); var serviceName = _replacer.Replace(downstreamReRoute.ServiceName, templatePlaceholderNameAndValues);
var pathTemplate = $"/{serviceName.Data.Value}{dsPath.Data.Value}"; var pathTemplate = $"/{serviceName.Data.Value}{dsPath.Data.Value}";
return (pathTemplate, query); return (pathTemplate, query);
} }
private static bool ServiceFabricRequest(DownstreamContext context) private static bool ServiceFabricRequest(IInternalConfiguration config, DownstreamReRoute downstreamReRoute)
{ {
return context.Configuration.ServiceProviderConfiguration.Type?.ToLower() == "servicefabric" && context.DownstreamReRoute.UseServiceDiscovery; return config.ServiceProviderConfiguration.Type?.ToLower() == "servicefabric" && downstreamReRoute.UseServiceDiscovery;
} }
} }
} }

View File

@ -1,11 +1,10 @@
using Microsoft.AspNetCore.Builder;
using Ocelot.Middleware.Pipeline;
namespace Ocelot.DownstreamUrlCreator.Middleware namespace Ocelot.DownstreamUrlCreator.Middleware
{ {
using Microsoft.AspNetCore.Builder;
public static class DownstreamUrlCreatorMiddlewareExtensions public static class DownstreamUrlCreatorMiddlewareExtensions
{ {
public static IOcelotPipelineBuilder UseDownstreamUrlCreatorMiddleware(this IOcelotPipelineBuilder builder) public static IApplicationBuilder UseDownstreamUrlCreatorMiddleware(this IApplicationBuilder builder)
{ {
return builder.UseMiddleware<DownstreamUrlCreatorMiddleware>(); return builder.UseMiddleware<DownstreamUrlCreatorMiddleware>();
} }

View File

@ -1,15 +1,19 @@
using System.Net;
namespace Ocelot.Errors namespace Ocelot.Errors
{ {
public abstract class Error public abstract class Error
{ {
protected Error(string message, OcelotErrorCode code) protected Error(string message, OcelotErrorCode code, int httpStatusCode)
{ {
HttpStatusCode = httpStatusCode;
Message = message; Message = message;
Code = code; Code = code;
} }
public string Message { get; private set; } public string Message { get; private set; }
public OcelotErrorCode Code { get; private set; } public OcelotErrorCode Code { get; private set; }
public int HttpStatusCode { get; private set; }
public override string ToString() public override string ToString()
{ {

View File

@ -1,102 +1,89 @@
namespace Ocelot.Errors.Middleware namespace Ocelot.Errors.Middleware
{ {
using Configuration; using Ocelot.Configuration;
using Ocelot.Configuration.Repository;
using Ocelot.Infrastructure.Extensions;
using Ocelot.Infrastructure.RequestData; using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.Middleware; using Ocelot.Middleware;
using System; using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Ocelot.DownstreamRouteFinder.Middleware;
/// <summary> /// <summary>
/// Catches all unhandled exceptions thrown by middleware, logs and returns a 500 /// Catches all unhandled exceptions thrown by middleware, logs and returns a 500.
/// </summary> /// </summary>
public class ExceptionHandlerMiddleware : OcelotMiddleware public class ExceptionHandlerMiddleware : OcelotMiddleware
{ {
private readonly OcelotRequestDelegate _next; private readonly RequestDelegate _next;
private readonly IInternalConfigurationRepository _configRepo;
private readonly IRequestScopedDataRepository _repo; private readonly IRequestScopedDataRepository _repo;
public ExceptionHandlerMiddleware(OcelotRequestDelegate next, public ExceptionHandlerMiddleware(RequestDelegate next,
IOcelotLoggerFactory loggerFactory, IOcelotLoggerFactory loggerFactory,
IInternalConfigurationRepository configRepo,
IRequestScopedDataRepository repo) IRequestScopedDataRepository repo)
: base(loggerFactory.CreateLogger<ExceptionHandlerMiddleware>()) : base(loggerFactory.CreateLogger<ExceptionHandlerMiddleware>())
{ {
_configRepo = configRepo;
_repo = repo;
_next = next; _next = next;
_repo = repo;
} }
public async Task Invoke(DownstreamContext context) public async Task Invoke(HttpContext httpContext)
{ {
try try
{ {
context.HttpContext.RequestAborted.ThrowIfCancellationRequested(); httpContext.RequestAborted.ThrowIfCancellationRequested();
//try and get the global request id and set it for logs... var internalConfiguration = httpContext.Items.IInternalConfiguration();
//should this basically be immutable per request...i guess it should!
//first thing is get config
var configuration = _configRepo.Get();
if (configuration.IsError) TrySetGlobalRequestId(httpContext, internalConfiguration);
{
throw new Exception($"{MiddlewareName} setting pipeline errors. IOcelotConfigurationProvider returned {configuration.Errors.ToErrorString()}");
}
TrySetGlobalRequestId(context, configuration.Data);
context.Configuration = configuration.Data;
Logger.LogDebug("ocelot pipeline started"); Logger.LogDebug("ocelot pipeline started");
await _next.Invoke(context); await _next.Invoke(httpContext);
} }
catch (OperationCanceledException) when (context.HttpContext.RequestAborted.IsCancellationRequested) catch (OperationCanceledException) when (httpContext.RequestAborted.IsCancellationRequested)
{ {
Logger.LogDebug("operation canceled"); Logger.LogDebug("operation canceled");
if (!context.HttpContext.Response.HasStarted) if (!httpContext.Response.HasStarted)
{ {
context.HttpContext.Response.StatusCode = 499; httpContext.Response.StatusCode = 499;
} }
} }
catch (Exception e) catch (Exception e)
{ {
Logger.LogDebug("error calling middleware"); Logger.LogDebug("error calling middleware");
var message = CreateMessage(context, e); var message = CreateMessage(httpContext, e);
Logger.LogError(message, e); Logger.LogError(message, e);
SetInternalServerErrorOnResponse(context); SetInternalServerErrorOnResponse(httpContext);
} }
Logger.LogDebug("ocelot pipeline finished"); Logger.LogDebug("ocelot pipeline finished");
} }
private void TrySetGlobalRequestId(DownstreamContext context, IInternalConfiguration configuration) private void TrySetGlobalRequestId(HttpContext httpContext, IInternalConfiguration configuration)
{ {
var key = configuration.RequestId; var key = configuration.RequestId;
if (!string.IsNullOrEmpty(key) && context.HttpContext.Request.Headers.TryGetValue(key, out var upstreamRequestIds)) if (!string.IsNullOrEmpty(key) && httpContext.Request.Headers.TryGetValue(key, out var upstreamRequestIds))
{ {
context.HttpContext.TraceIdentifier = upstreamRequestIds.First(); httpContext.TraceIdentifier = upstreamRequestIds.First();
} }
_repo.Add("RequestId", context.HttpContext.TraceIdentifier); _repo.Add("RequestId", httpContext.TraceIdentifier);
} }
private void SetInternalServerErrorOnResponse(DownstreamContext context) private void SetInternalServerErrorOnResponse(HttpContext httpContext)
{ {
if (!context.HttpContext.Response.HasStarted) if (!httpContext.Response.HasStarted)
{ {
context.HttpContext.Response.StatusCode = 500; httpContext.Response.StatusCode = 500;
} }
} }
private string CreateMessage(DownstreamContext context, Exception e) private string CreateMessage(HttpContext httpContext, Exception e)
{ {
var message = var message =
$"Exception caught in global error handler, exception message: {e.Message}, exception stack: {e.StackTrace}"; $"Exception caught in global error handler, exception message: {e.Message}, exception stack: {e.StackTrace}";
@ -107,7 +94,7 @@ namespace Ocelot.Errors.Middleware
$"{message}, inner exception message {e.InnerException.Message}, inner exception stack {e.InnerException.StackTrace}"; $"{message}, inner exception message {e.InnerException.Message}, inner exception stack {e.InnerException.StackTrace}";
} }
return $"{message} RequestId: {context.HttpContext.TraceIdentifier}"; return $"{message} RequestId: {httpContext.TraceIdentifier}";
} }
} }
} }

View File

@ -1,11 +1,11 @@
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Ocelot.Middleware.Pipeline; using Ocelot.Middleware;
namespace Ocelot.Errors.Middleware namespace Ocelot.Errors.Middleware
{ {
public static class ExceptionHandlerMiddlewareExtensions public static class ExceptionHandlerMiddlewareExtensions
{ {
public static IOcelotPipelineBuilder UseExceptionHandlerMiddleware(this IOcelotPipelineBuilder builder) public static IApplicationBuilder UseExceptionHandlerMiddleware(this IApplicationBuilder builder)
{ {
return builder.UseMiddleware<ExceptionHandlerMiddleware>(); return builder.UseMiddleware<ExceptionHandlerMiddleware>();
} }

View File

@ -7,6 +7,8 @@ namespace Ocelot.Headers
using Ocelot.Responses; using Ocelot.Responses;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.AspNetCore.Http;
using Ocelot.DownstreamRouteFinder.Middleware;
public class HttpResponseHeaderReplacer : IHttpResponseHeaderReplacer public class HttpResponseHeaderReplacer : IHttpResponseHeaderReplacer
{ {
@ -17,10 +19,10 @@ namespace Ocelot.Headers
_placeholders = placeholders; _placeholders = placeholders;
} }
public Response Replace(DownstreamContext context, List<HeaderFindAndReplace> fAndRs) public Response Replace(HttpContext httpContext, List<HeaderFindAndReplace> fAndRs)
{ {
var response = context.DownstreamResponse; var response = httpContext.Items.DownstreamResponse();
var request = context.DownstreamRequest; var request = httpContext.Items.DownstreamRequest();
foreach (var f in fAndRs) foreach (var f in fAndRs)
{ {

View File

@ -1,12 +1,12 @@
namespace Ocelot.Headers namespace Ocelot.Headers
{ {
using Ocelot.Configuration; using Ocelot.Configuration;
using Ocelot.Middleware;
using Ocelot.Responses; using Ocelot.Responses;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Http;
public interface IHttpResponseHeaderReplacer public interface IHttpResponseHeaderReplacer
{ {
Response Replace(DownstreamContext context, List<HeaderFindAndReplace> fAndRs); public Response Replace(HttpContext httpContext, List<HeaderFindAndReplace> fAndRs);
} }
} }

View File

@ -1,16 +1,18 @@
using Ocelot.Logging; namespace Ocelot.Headers.Middleware
using Ocelot.Middleware;
using System.Linq;
using System.Threading.Tasks;
namespace Ocelot.Headers.Middleware
{ {
using Microsoft.AspNetCore.Http;
using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.Logging;
using Ocelot.Middleware;
using System.Linq;
using System.Threading.Tasks;
public class ClaimsToHeadersMiddleware : OcelotMiddleware public class ClaimsToHeadersMiddleware : OcelotMiddleware
{ {
private readonly OcelotRequestDelegate _next; private readonly RequestDelegate _next;
private readonly IAddHeadersToRequest _addHeadersToRequest; private readonly IAddHeadersToRequest _addHeadersToRequest;
public ClaimsToHeadersMiddleware(OcelotRequestDelegate next, public ClaimsToHeadersMiddleware(RequestDelegate next,
IOcelotLoggerFactory loggerFactory, IOcelotLoggerFactory loggerFactory,
IAddHeadersToRequest addHeadersToRequest) IAddHeadersToRequest addHeadersToRequest)
: base(loggerFactory.CreateLogger<ClaimsToHeadersMiddleware>()) : base(loggerFactory.CreateLogger<ClaimsToHeadersMiddleware>())
@ -19,26 +21,30 @@ namespace Ocelot.Headers.Middleware
_addHeadersToRequest = addHeadersToRequest; _addHeadersToRequest = addHeadersToRequest;
} }
public async Task Invoke(DownstreamContext context) public async Task Invoke(HttpContext httpContext)
{ {
if (context.DownstreamReRoute.ClaimsToHeaders.Any()) var downstreamReRoute = httpContext.Items.DownstreamReRoute();
{
Logger.LogInformation($"{context.DownstreamReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to headers");
var response = _addHeadersToRequest.SetHeadersOnDownstreamRequest(context.DownstreamReRoute.ClaimsToHeaders, context.HttpContext.User.Claims, context.DownstreamRequest); if (downstreamReRoute.ClaimsToHeaders.Any())
{
Logger.LogInformation($"{downstreamReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to headers");
var downstreamRequest = httpContext.Items.DownstreamRequest();
var response = _addHeadersToRequest.SetHeadersOnDownstreamRequest(downstreamReRoute.ClaimsToHeaders, httpContext.User.Claims, downstreamRequest);
if (response.IsError) if (response.IsError)
{ {
Logger.LogWarning("Error setting headers on context, setting pipeline error"); Logger.LogWarning("Error setting headers on context, setting pipeline error");
SetPipelineError(context, response.Errors); httpContext.Items.UpsertErrors(response.Errors);
return; return;
} }
Logger.LogInformation("headers have been set on context"); Logger.LogInformation("headers have been set on context");
} }
await _next.Invoke(context); await _next.Invoke(httpContext);
} }
} }
} }

View File

@ -1,11 +1,10 @@
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Ocelot.Middleware.Pipeline;
namespace Ocelot.Headers.Middleware namespace Ocelot.Headers.Middleware
{ {
public static class ClaimsToHeadersMiddlewareExtensions public static class ClaimsToHeadersMiddlewareExtensions
{ {
public static IOcelotPipelineBuilder UseClaimsToHeadersMiddleware(this IOcelotPipelineBuilder builder) public static IApplicationBuilder UseClaimsToHeadersMiddleware(this IApplicationBuilder builder)
{ {
return builder.UseMiddleware<ClaimsToHeadersMiddleware>(); return builder.UseMiddleware<ClaimsToHeadersMiddleware>();
} }

View File

@ -1,23 +1,26 @@
using Ocelot.Logging;
using Ocelot.Middleware;
using System.Threading.Tasks;
namespace Ocelot.Headers.Middleware namespace Ocelot.Headers.Middleware
{ {
using Microsoft.AspNetCore.Http;
using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.Logging;
using Ocelot.Middleware;
using System.Threading.Tasks;
public class HttpHeadersTransformationMiddleware : OcelotMiddleware public class HttpHeadersTransformationMiddleware : OcelotMiddleware
{ {
private readonly OcelotRequestDelegate _next; private readonly RequestDelegate _next;
private readonly IHttpContextRequestHeaderReplacer _preReplacer; private readonly IHttpContextRequestHeaderReplacer _preReplacer;
private readonly IHttpResponseHeaderReplacer _postReplacer; private readonly IHttpResponseHeaderReplacer _postReplacer;
private readonly IAddHeadersToResponse _addHeadersToResponse; private readonly IAddHeadersToResponse _addHeadersToResponse;
private readonly IAddHeadersToRequest _addHeadersToRequest; private readonly IAddHeadersToRequest _addHeadersToRequest;
public HttpHeadersTransformationMiddleware(OcelotRequestDelegate next, public HttpHeadersTransformationMiddleware(RequestDelegate next,
IOcelotLoggerFactory loggerFactory, IOcelotLoggerFactory loggerFactory,
IHttpContextRequestHeaderReplacer preReplacer, IHttpContextRequestHeaderReplacer preReplacer,
IHttpResponseHeaderReplacer postReplacer, IHttpResponseHeaderReplacer postReplacer,
IAddHeadersToResponse addHeadersToResponse, IAddHeadersToResponse addHeadersToResponse,
IAddHeadersToRequest addHeadersToRequest) IAddHeadersToRequest addHeadersToRequest
)
: base(loggerFactory.CreateLogger<HttpHeadersTransformationMiddleware>()) : base(loggerFactory.CreateLogger<HttpHeadersTransformationMiddleware>())
{ {
_addHeadersToResponse = addHeadersToResponse; _addHeadersToResponse = addHeadersToResponse;
@ -27,27 +30,33 @@ namespace Ocelot.Headers.Middleware
_preReplacer = preReplacer; _preReplacer = preReplacer;
} }
public async Task Invoke(DownstreamContext context) public async Task Invoke(HttpContext httpContext)
{ {
var preFAndRs = context.DownstreamReRoute.UpstreamHeadersFindAndReplace; var downstreamReRoute = httpContext.Items.DownstreamReRoute();
var preFAndRs = downstreamReRoute.UpstreamHeadersFindAndReplace;
//todo - this should be on httprequestmessage not httpcontext? //todo - this should be on httprequestmessage not httpcontext?
_preReplacer.Replace(context.HttpContext, preFAndRs); _preReplacer.Replace(httpContext, preFAndRs);
_addHeadersToRequest.SetHeadersOnDownstreamRequest(context.DownstreamReRoute.AddHeadersToUpstream, context.HttpContext); _addHeadersToRequest.SetHeadersOnDownstreamRequest(downstreamReRoute.AddHeadersToUpstream, httpContext);
await _next.Invoke(context); await _next.Invoke(httpContext);
if (context.IsError) // todo check errors is ok
//todo put this check on the base class?
if (httpContext.Items.Errors().Count > 0)
{ {
return; return;
} }
var postFAndRs = context.DownstreamReRoute.DownstreamHeadersFindAndReplace; var postFAndRs = downstreamReRoute.DownstreamHeadersFindAndReplace;
_postReplacer.Replace(context, postFAndRs); _postReplacer.Replace(httpContext, postFAndRs);
_addHeadersToResponse.Add(context.DownstreamReRoute.AddHeadersToDownstream, context.DownstreamResponse); var downstreamResponse = httpContext.Items.DownstreamResponse();
_addHeadersToResponse.Add(downstreamReRoute.AddHeadersToDownstream, downstreamResponse);
} }
} }
} }

View File

@ -1,11 +1,10 @@
using Microsoft.AspNetCore.Builder; namespace Ocelot.Headers.Middleware
using Ocelot.Middleware.Pipeline;
namespace Ocelot.Headers.Middleware
{ {
using Microsoft.AspNetCore.Builder;
public static class HttpHeadersTransformationMiddlewareExtensions public static class HttpHeadersTransformationMiddlewareExtensions
{ {
public static IOcelotPipelineBuilder UseHttpHeadersTransformationMiddleware(this IOcelotPipelineBuilder builder) public static IApplicationBuilder UseHttpHeadersTransformationMiddleware(this IApplicationBuilder builder)
{ {
return builder.UseMiddleware<HttpHeadersTransformationMiddleware>(); return builder.UseMiddleware<HttpHeadersTransformationMiddleware>();
} }

View File

@ -5,7 +5,7 @@ namespace Ocelot.Infrastructure
public class CannotAddPlaceholderError : Error public class CannotAddPlaceholderError : Error
{ {
public CannotAddPlaceholderError(string message) public CannotAddPlaceholderError(string message)
: base(message, OcelotErrorCode.CannotAddPlaceholderError) : base(message, OcelotErrorCode.CannotAddPlaceholderError, 404)
{ {
} }
} }

View File

@ -5,7 +5,7 @@ namespace Ocelot.Infrastructure
public class CannotRemovePlaceholderError : Error public class CannotRemovePlaceholderError : Error
{ {
public CannotRemovePlaceholderError(string message) public CannotRemovePlaceholderError(string message)
: base(message, OcelotErrorCode.CannotRemovePlaceholderError) : base(message, OcelotErrorCode.CannotRemovePlaceholderError, 404)
{ {
} }
} }

View File

@ -1,11 +1,11 @@
namespace Ocelot.Infrastructure.Claims.Parser namespace Ocelot.Infrastructure.Claims.Parser
{ {
using Errors; using Ocelot.Errors;
public class CannotFindClaimError : Error public class CannotFindClaimError : Error
{ {
public CannotFindClaimError(string message) public CannotFindClaimError(string message)
: base(message, OcelotErrorCode.CannotFindClaimError) : base(message, OcelotErrorCode.CannotFindClaimError, 403)
{ {
} }
} }

View File

@ -5,7 +5,7 @@ namespace Ocelot.Infrastructure
public class CouldNotFindPlaceholderError : Error public class CouldNotFindPlaceholderError : Error
{ {
public CouldNotFindPlaceholderError(string placeholder) public CouldNotFindPlaceholderError(string placeholder)
: base($"Unable to find placeholder called {placeholder}", OcelotErrorCode.CouldNotFindPlaceholderError) : base($"Unable to find placeholder called {placeholder}", OcelotErrorCode.CouldNotFindPlaceholderError, 404)
{ {
} }
} }

View File

@ -4,7 +4,7 @@ namespace Ocelot.Infrastructure.RequestData
{ {
public class CannotAddDataError : Error public class CannotAddDataError : Error
{ {
public CannotAddDataError(string message) : base(message, OcelotErrorCode.CannotAddDataError) public CannotAddDataError(string message) : base(message, OcelotErrorCode.CannotAddDataError, 404)
{ {
} }
} }

View File

@ -4,7 +4,7 @@ namespace Ocelot.Infrastructure.RequestData
{ {
public class CannotFindDataError : Error public class CannotFindDataError : Error
{ {
public CannotFindDataError(string message) : base(message, OcelotErrorCode.CannotFindDataError) public CannotFindDataError(string message) : base(message, OcelotErrorCode.CannotFindDataError, 404)
{ {
} }
} }

View File

@ -2,11 +2,12 @@ namespace Ocelot.LoadBalancer.LoadBalancers
{ {
using Ocelot.Infrastructure; using Ocelot.Infrastructure;
using Ocelot.Middleware; using Ocelot.Middleware;
using Responses; using Ocelot.Responses;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Threading.Tasks; using System.Threading.Tasks;
using Values; using Microsoft.AspNetCore.Http;
using Ocelot.Values;
public class CookieStickySessions : ILoadBalancer public class CookieStickySessions : ILoadBalancer
{ {
@ -41,9 +42,9 @@ namespace Ocelot.LoadBalancer.LoadBalancers
}); });
} }
public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext context) public async Task<Response<ServiceHostAndPort>> Lease(HttpContext httpContext)
{ {
var key = context.HttpContext.Request.Cookies[_key]; var key = httpContext.Request.Cookies[_key];
lock (_lock) lock (_lock)
{ {
@ -61,7 +62,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers
} }
} }
var next = await _loadBalancer.Lease(context); var next = await _loadBalancer.Lease(httpContext);
if (next.IsError) if (next.IsError)
{ {

View File

@ -5,7 +5,7 @@
public class CouldNotFindLoadBalancerCreator : Error public class CouldNotFindLoadBalancerCreator : Error
{ {
public CouldNotFindLoadBalancerCreator(string message) public CouldNotFindLoadBalancerCreator(string message)
: base(message, OcelotErrorCode.CouldNotFindLoadBalancerCreator) : base(message, OcelotErrorCode.CouldNotFindLoadBalancerCreator, 404)
{ {
} }
} }

View File

@ -5,7 +5,7 @@
public class ErrorInvokingLoadBalancerCreator : Error public class ErrorInvokingLoadBalancerCreator : Error
{ {
public ErrorInvokingLoadBalancerCreator(Exception e) : base($"Error when invoking user provided load balancer creator function, Message: {e.Message}, StackTrace: {e.StackTrace}", OcelotErrorCode.ErrorInvokingLoadBalancerCreator) public ErrorInvokingLoadBalancerCreator(Exception e) : base($"Error when invoking user provided load balancer creator function, Message: {e.Message}, StackTrace: {e.StackTrace}", OcelotErrorCode.ErrorInvokingLoadBalancerCreator, 500)
{ {
} }
} }

View File

@ -1,13 +1,13 @@
using Ocelot.Middleware;
using Ocelot.Responses;
using Ocelot.Values;
using System.Threading.Tasks;
namespace Ocelot.LoadBalancer.LoadBalancers namespace Ocelot.LoadBalancer.LoadBalancers
{ {
using Microsoft.AspNetCore.Http;
using Ocelot.Responses;
using Ocelot.Values;
using System.Threading.Tasks;
public interface ILoadBalancer public interface ILoadBalancer
{ {
Task<Response<ServiceHostAndPort>> Lease(DownstreamContext context); Task<Response<ServiceHostAndPort>> Lease(HttpContext httpContext);
void Release(ServiceHostAndPort hostAndPort); void Release(ServiceHostAndPort hostAndPort);
} }

View File

@ -1,13 +1,14 @@
using Ocelot.Middleware; namespace Ocelot.LoadBalancer.LoadBalancers
using Ocelot.Responses;
using Ocelot.Values;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Ocelot.LoadBalancer.LoadBalancers
{ {
using Microsoft.AspNetCore.Http;
using Ocelot.Middleware;
using Ocelot.Responses;
using Ocelot.Values;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
public class LeastConnection : ILoadBalancer public class LeastConnection : ILoadBalancer
{ {
private readonly Func<Task<List<Service>>> _services; private readonly Func<Task<List<Service>>> _services;
@ -22,7 +23,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers
_leases = new List<Lease>(); _leases = new List<Lease>();
} }
public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext downstreamContext) public async Task<Response<ServiceHostAndPort>> Lease(HttpContext httpContext)
{ {
var services = await _services.Invoke(); var services = await _services.Invoke();

View File

@ -1,13 +1,14 @@
using Ocelot.Middleware; namespace Ocelot.LoadBalancer.LoadBalancers
using Ocelot.Responses;
using Ocelot.Values;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Ocelot.LoadBalancer.LoadBalancers
{ {
using Microsoft.AspNetCore.Http;
using Ocelot.Middleware;
using Ocelot.Responses;
using Ocelot.Values;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
public class NoLoadBalancer : ILoadBalancer public class NoLoadBalancer : ILoadBalancer
{ {
private readonly Func<Task<List<Service>>> _services; private readonly Func<Task<List<Service>>> _services;
@ -17,7 +18,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers
_services = services; _services = services;
} }
public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext downstreamContext) public async Task<Response<ServiceHostAndPort>> Lease(HttpContext httpContext)
{ {
var services = await _services(); var services = await _services();

View File

@ -6,6 +6,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
public class RoundRobin : ILoadBalancer public class RoundRobin : ILoadBalancer
{ {
@ -19,7 +20,7 @@
_services = services; _services = services;
} }
public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext downstreamContext) public async Task<Response<ServiceHostAndPort>> Lease(HttpContext httpContext)
{ {
var services = await _services(); var services = await _services();
lock (_lock) lock (_lock)

View File

@ -5,7 +5,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers
public class ServicesAreEmptyError : Error public class ServicesAreEmptyError : Error
{ {
public ServicesAreEmptyError(string message) public ServicesAreEmptyError(string message)
: base(message, OcelotErrorCode.ServicesAreEmptyError) : base(message, OcelotErrorCode.ServicesAreEmptyError, 404)
{ {
} }
} }

View File

@ -5,7 +5,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers
public class ServicesAreNullError : Error public class ServicesAreNullError : Error
{ {
public ServicesAreNullError(string message) public ServicesAreNullError(string message)
: base(message, OcelotErrorCode.ServicesAreNullError) : base(message, OcelotErrorCode.ServicesAreNullError, 404)
{ {
} }
} }

View File

@ -5,7 +5,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers
public class UnableToFindLoadBalancerError : Errors.Error public class UnableToFindLoadBalancerError : Errors.Error
{ {
public UnableToFindLoadBalancerError(string message) public UnableToFindLoadBalancerError(string message)
: base(message, OcelotErrorCode.UnableToFindLoadBalancerError) : base(message, OcelotErrorCode.UnableToFindLoadBalancerError, 404)
{ {
} }
} }

View File

@ -1,17 +1,19 @@
using Ocelot.LoadBalancer.LoadBalancers; namespace Ocelot.LoadBalancer.Middleware
using Ocelot.Logging;
using Ocelot.Middleware;
using System;
using System.Threading.Tasks;
namespace Ocelot.LoadBalancer.Middleware
{ {
using Microsoft.AspNetCore.Http;
using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.Logging;
using Ocelot.Middleware;
using System;
using System.Threading.Tasks;
public class LoadBalancingMiddleware : OcelotMiddleware public class LoadBalancingMiddleware : OcelotMiddleware
{ {
private readonly OcelotRequestDelegate _next; private readonly RequestDelegate _next;
private readonly ILoadBalancerHouse _loadBalancerHouse; private readonly ILoadBalancerHouse _loadBalancerHouse;
public LoadBalancingMiddleware(OcelotRequestDelegate next, public LoadBalancingMiddleware(RequestDelegate next,
IOcelotLoggerFactory loggerFactory, IOcelotLoggerFactory loggerFactory,
ILoadBalancerHouse loadBalancerHouse) ILoadBalancerHouse loadBalancerHouse)
: base(loggerFactory.CreateLogger<LoadBalancingMiddleware>()) : base(loggerFactory.CreateLogger<LoadBalancingMiddleware>())
@ -20,39 +22,47 @@ namespace Ocelot.LoadBalancer.Middleware
_loadBalancerHouse = loadBalancerHouse; _loadBalancerHouse = loadBalancerHouse;
} }
public async Task Invoke(DownstreamContext context) public async Task Invoke(HttpContext httpContext)
{ {
var loadBalancer = _loadBalancerHouse.Get(context.DownstreamReRoute, context.Configuration.ServiceProviderConfiguration); var downstreamReRoute = httpContext.Items.DownstreamReRoute();
var internalConfiguration = httpContext.Items.IInternalConfiguration();
var loadBalancer = _loadBalancerHouse.Get(downstreamReRoute, internalConfiguration.ServiceProviderConfiguration);
if (loadBalancer.IsError) if (loadBalancer.IsError)
{ {
Logger.LogDebug("there was an error retriving the loadbalancer, setting pipeline error"); Logger.LogDebug("there was an error retriving the loadbalancer, setting pipeline error");
SetPipelineError(context, loadBalancer.Errors); httpContext.Items.UpsertErrors(loadBalancer.Errors);
return; return;
} }
var hostAndPort = await loadBalancer.Data.Lease(context); var hostAndPort = await loadBalancer.Data.Lease(httpContext);
if (hostAndPort.IsError) if (hostAndPort.IsError)
{ {
Logger.LogDebug("there was an error leasing the loadbalancer, setting pipeline error"); Logger.LogDebug("there was an error leasing the loadbalancer, setting pipeline error");
SetPipelineError(context, hostAndPort.Errors); httpContext.Items.UpsertErrors(hostAndPort.Errors);
return; return;
} }
context.DownstreamRequest.Host = hostAndPort.Data.DownstreamHost; var downstreamRequest = httpContext.Items.DownstreamRequest();
//todo check downstreamRequest is ok
downstreamRequest.Host = hostAndPort.Data.DownstreamHost;
if (hostAndPort.Data.DownstreamPort > 0) if (hostAndPort.Data.DownstreamPort > 0)
{ {
context.DownstreamRequest.Port = hostAndPort.Data.DownstreamPort; downstreamRequest.Port = hostAndPort.Data.DownstreamPort;
} }
if (!string.IsNullOrEmpty(hostAndPort.Data.Scheme)) if (!string.IsNullOrEmpty(hostAndPort.Data.Scheme))
{ {
context.DownstreamRequest.Scheme = hostAndPort.Data.Scheme; downstreamRequest.Scheme = hostAndPort.Data.Scheme;
} }
try try
{ {
await _next.Invoke(context); await _next.Invoke(httpContext);
} }
catch (Exception) catch (Exception)
{ {

View File

@ -1,11 +1,10 @@
using Microsoft.AspNetCore.Builder;
using Ocelot.Middleware.Pipeline;
namespace Ocelot.LoadBalancer.Middleware namespace Ocelot.LoadBalancer.Middleware
{ {
using Microsoft.AspNetCore.Builder;
public static class LoadBalancingMiddlewareExtensions public static class LoadBalancingMiddlewareExtensions
{ {
public static IOcelotPipelineBuilder UseLoadBalancingMiddleware(this IOcelotPipelineBuilder builder) public static IApplicationBuilder UseLoadBalancingMiddleware(this IApplicationBuilder builder)
{ {
return builder.UseMiddleware<LoadBalancingMiddleware>(); return builder.UseMiddleware<LoadBalancingMiddleware>();
} }

View File

@ -1,11 +1,10 @@
using Microsoft.AspNetCore.Http; namespace Ocelot.Logging
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DiagnosticAdapter;
using Ocelot.Middleware;
using System;
namespace Ocelot.Logging
{ {
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DiagnosticAdapter;
using System;
public class OcelotDiagnosticListener public class OcelotDiagnosticListener
{ {
private readonly IOcelotLogger _logger; private readonly IOcelotLogger _logger;
@ -17,27 +16,6 @@ namespace Ocelot.Logging
_tracer = serviceProvider.GetService<ITracer>(); _tracer = serviceProvider.GetService<ITracer>();
} }
[DiagnosticName("Ocelot.MiddlewareException")]
public virtual void OcelotMiddlewareException(Exception exception, DownstreamContext context, string name)
{
_logger.LogTrace($"Ocelot.MiddlewareException: {name}; {exception.Message};");
Event(context.HttpContext, $"Ocelot.MiddlewareStarted: {name}; {context.HttpContext.Request.Path}");
}
[DiagnosticName("Ocelot.MiddlewareStarted")]
public virtual void OcelotMiddlewareStarted(DownstreamContext context, string name)
{
_logger.LogTrace($"Ocelot.MiddlewareStarted: {name}; {context.HttpContext.Request.Path}");
Event(context.HttpContext, $"Ocelot.MiddlewareStarted: {name}; {context.HttpContext.Request.Path}");
}
[DiagnosticName("Ocelot.MiddlewareFinished")]
public virtual void OcelotMiddlewareFinished(DownstreamContext context, string name)
{
_logger.LogTrace($"Ocelot.MiddlewareFinished: {name}; {context.HttpContext.Request.Path}");
Event(context.HttpContext, $"OcelotMiddlewareFinished: {name}; {context.HttpContext.Request.Path}");
}
[DiagnosticName("Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareStarting")] [DiagnosticName("Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareStarting")]
public virtual void OnMiddlewareStarting(HttpContext httpContext, string name) public virtual void OnMiddlewareStarting(HttpContext httpContext, string name)
{ {

View File

@ -0,0 +1,37 @@
namespace Ocelot.Middleware
{
using System.Threading.Tasks;
using Ocelot.Errors.Middleware;
using Ocelot.Configuration.Repository;
using Ocelot.Logging;
using Microsoft.AspNetCore.Http;
using Ocelot.DownstreamRouteFinder.Middleware;
public class ConfigurationMiddleware : OcelotMiddleware
{
private readonly RequestDelegate _next;
private readonly IInternalConfigurationRepository _configRepo;
public ConfigurationMiddleware(RequestDelegate next, IOcelotLoggerFactory loggerFactory, IInternalConfigurationRepository configRepo)
: base(loggerFactory.CreateLogger<ExceptionHandlerMiddleware>())
{
_next = next;
_configRepo = configRepo;
}
public async Task Invoke(HttpContext httpContext)
{
//todo check the config is actually ok?
var config = _configRepo.Get();
if(config.IsError)
{
throw new System.Exception("OOOOPS this should not happen raise an issue in GitHub");
}
httpContext.Items.SetIInternalConfiguration(config.Data);
await _next.Invoke(httpContext);
}
}
}

View File

@ -1,34 +0,0 @@
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Errors;
using Ocelot.Request.Middleware;
using System.Collections.Generic;
namespace Ocelot.Middleware
{
public class DownstreamContext
{
public DownstreamContext(HttpContext httpContext)
{
HttpContext = httpContext;
Errors = new List<Error>();
}
public List<PlaceholderNameAndValue> TemplatePlaceholderNameAndValues { get; set; }
public HttpContext HttpContext { get; }
public DownstreamReRoute DownstreamReRoute { get; set; }
public DownstreamRequest DownstreamRequest { get; set; }
public DownstreamResponse DownstreamResponse { get; set; }
public List<Error> Errors { get; }
public IInternalConfiguration Configuration { get; set; }
public bool IsError => Errors.Count > 0;
}
}

View File

@ -0,0 +1,12 @@
namespace Ocelot.Middleware
{
using Microsoft.AspNetCore.Builder;
public static class DownstreamContextMiddlewareExtensions
{
public static IApplicationBuilder UseDownstreamContextMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<ConfigurationMiddleware>();
}
}
}

View File

@ -0,0 +1,117 @@
namespace Ocelot.Middleware
{
using Ocelot.Configuration;
using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Errors;
using Ocelot.Request.Middleware;
using System.Collections.Generic;
public static class HttpItemsExtensions
{
public static void UpsertDownstreamRequest(this IDictionary<object, object> input, DownstreamRequest downstreamRequest)
{
input.Upsert("DownstreamRequest", downstreamRequest);
}
public static void UpsertDownstreamResponse(this IDictionary<object, object> input, DownstreamResponse downstreamResponse)
{
input.Upsert("DownstreamResponse", downstreamResponse);
}
public static void UpsertDownstreamReRoute(this IDictionary<object, object> input, DownstreamReRoute downstreamReRoute)
{
input.Upsert("DownstreamReRoute", downstreamReRoute);
}
public static void UpsertTemplatePlaceholderNameAndValues(this IDictionary<object, object> input, List<PlaceholderNameAndValue> tPNV)
{
input.Upsert("TemplatePlaceholderNameAndValues", tPNV);
}
public static void UpsertDownstreamRoute(this IDictionary<object, object> input, DownstreamRoute downstreamRoute)
{
input.Upsert("DownstreamRoute", downstreamRoute);
}
public static void UpsertErrors(this IDictionary<object, object> input, List<Error> errors)
{
input.Upsert("Errors", errors);
}
public static void SetError(this IDictionary<object, object> input, Error error)
{
var errors = new List<Error>() { error };
input.Upsert("Errors", errors);
}
public static void SetIInternalConfiguration(this IDictionary<object, object> input, IInternalConfiguration config)
{
input.Upsert("IInternalConfiguration", config);
}
public static IInternalConfiguration IInternalConfiguration(this IDictionary<object, object> input)
{
return input.Get<IInternalConfiguration>("IInternalConfiguration");
}
public static List<Error> Errors(this IDictionary<object, object> input)
{
var errors = input.Get<List<Error>>("Errors");
return errors == null ? new List<Error>() : errors;
}
public static DownstreamRoute DownstreamRoute(this IDictionary<object, object> input)
{
return input.Get<DownstreamRoute>("DownstreamRoute");
}
public static List<PlaceholderNameAndValue> TemplatePlaceholderNameAndValues(this IDictionary<object, object> input)
{
return input.Get<List<PlaceholderNameAndValue>>("TemplatePlaceholderNameAndValues");
}
public static DownstreamRequest DownstreamRequest(this IDictionary<object, object> input)
{
return input.Get<DownstreamRequest>("DownstreamRequest");
}
public static DownstreamResponse DownstreamResponse(this IDictionary<object, object> input)
{
return input.Get<DownstreamResponse>("DownstreamResponse");
}
public static DownstreamReRoute DownstreamReRoute(this IDictionary<object, object> input)
{
return input.Get<DownstreamReRoute>("DownstreamReRoute");
}
private static T Get<T>(this IDictionary<object, object> input, string key)
{
if (input.TryGetValue(key, out var value))
{
return (T)value;
}
return default(T);
}
private static void Upsert<T>(this IDictionary<object, object> input, string key, T value)
{
if (input.DoesntExist(key))
{
input.Add(key, value);
}
else
{
input.Remove(key);
input.Add(key, value);
}
}
private static bool DoesntExist(this IDictionary<object, object> input, string key)
{
return !input.ContainsKey(key);
}
}
}

View File

@ -1,10 +0,0 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Ocelot.Middleware.Multiplexer
{
public interface IDefinedAggregator
{
Task<DownstreamResponse> Aggregate(List<DownstreamContext> responses);
}
}

View File

@ -1,10 +0,0 @@
using Ocelot.Configuration;
using System.Threading.Tasks;
namespace Ocelot.Middleware.Multiplexer
{
public interface IMultiplexer
{
Task Multiplex(DownstreamContext context, ReRoute reRoute, OcelotRequestDelegate next);
}
}

View File

@ -1,11 +0,0 @@
using Ocelot.Configuration;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Ocelot.Middleware.Multiplexer
{
public interface IResponseAggregator
{
Task Aggregate(ReRoute reRoute, DownstreamContext originalContext, List<DownstreamContext> downstreamResponses);
}
}

View File

@ -1,152 +0,0 @@
using Ocelot.Configuration;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Ocelot.Middleware.Multiplexer
{
public class Multiplexer : IMultiplexer
{
private readonly IResponseAggregatorFactory _factory;
public Multiplexer(IResponseAggregatorFactory factory)
{
_factory = factory;
}
public async Task Multiplex(DownstreamContext context, ReRoute reRoute, OcelotRequestDelegate next)
{
var reRouteKeysConfigs = reRoute.DownstreamReRouteConfig;
if (reRouteKeysConfigs == null || !reRouteKeysConfigs.Any())
{
var tasks = new Task<DownstreamContext>[reRoute.DownstreamReRoute.Count];
for (var i = 0; i < reRoute.DownstreamReRoute.Count; i++)
{
var downstreamContext = new DownstreamContext(context.HttpContext)
{
TemplatePlaceholderNameAndValues = context.TemplatePlaceholderNameAndValues,
Configuration = context.Configuration,
DownstreamReRoute = reRoute.DownstreamReRoute[i],
};
tasks[i] = Fire(downstreamContext, next);
}
await Task.WhenAll(tasks);
var contexts = new List<DownstreamContext>();
foreach (var task in tasks)
{
var finished = await task;
contexts.Add(finished);
}
await Map(reRoute, context, contexts);
}
else
{
var downstreamContextMain = new DownstreamContext(context.HttpContext)
{
TemplatePlaceholderNameAndValues = context.TemplatePlaceholderNameAndValues,
Configuration = context.Configuration,
DownstreamReRoute = reRoute.DownstreamReRoute[0],
};
var mainResponse = await Fire(downstreamContextMain, next);
if (reRoute.DownstreamReRoute.Count == 1)
{
MapNotAggregate(context, new List<DownstreamContext>() { mainResponse });
return;
}
var tasks = new List<Task<DownstreamContext>>();
if (mainResponse.DownstreamResponse == null)
{
return;
}
var content = await mainResponse.DownstreamResponse.Content.ReadAsStringAsync();
var jObject = Newtonsoft.Json.Linq.JToken.Parse(content);
for (var i = 1; i < reRoute.DownstreamReRoute.Count; i++)
{
var templatePlaceholderNameAndValues = context.TemplatePlaceholderNameAndValues;
var downstreamReRoute = reRoute.DownstreamReRoute[i];
var matchAdvancedAgg = reRouteKeysConfigs.FirstOrDefault(q => q.ReRouteKey == downstreamReRoute.Key);
if (matchAdvancedAgg != null)
{
var values = jObject.SelectTokens(matchAdvancedAgg.JsonPath).Select(s => s.ToString()).Distinct().ToList();
foreach (var value in values)
{
var downstreamContext = new DownstreamContext(context.HttpContext)
{
TemplatePlaceholderNameAndValues = new List<PlaceholderNameAndValue>(templatePlaceholderNameAndValues),
Configuration = context.Configuration,
DownstreamReRoute = downstreamReRoute,
};
downstreamContext.TemplatePlaceholderNameAndValues.Add(new PlaceholderNameAndValue("{" + matchAdvancedAgg.Parameter + "}", value.ToString()));
tasks.Add(Fire(downstreamContext, next));
}
}
else
{
var downstreamContext = new DownstreamContext(context.HttpContext)
{
TemplatePlaceholderNameAndValues = new List<PlaceholderNameAndValue>(templatePlaceholderNameAndValues),
Configuration = context.Configuration,
DownstreamReRoute = downstreamReRoute,
};
tasks.Add(Fire(downstreamContext, next));
}
}
await Task.WhenAll(tasks);
var contexts = new List<DownstreamContext>() { mainResponse };
foreach (var task in tasks)
{
var finished = await task;
contexts.Add(finished);
}
await Map(reRoute, context, contexts);
}
}
private async Task Map(ReRoute reRoute, DownstreamContext context, List<DownstreamContext> contexts)
{
if (reRoute.DownstreamReRoute.Count > 1)
{
var aggregator = _factory.Get(reRoute);
await aggregator.Aggregate(reRoute, context, contexts);
}
else
{
MapNotAggregate(context, contexts);
}
}
private void MapNotAggregate(DownstreamContext originalContext, List<DownstreamContext> downstreamContexts)
{
//assume at least one..if this errors then it will be caught by global exception handler
var finished = downstreamContexts.First();
originalContext.Errors.AddRange(finished.Errors);
originalContext.DownstreamRequest = finished.DownstreamRequest;
originalContext.DownstreamResponse = finished.DownstreamResponse;
}
private async Task<DownstreamContext> Fire(DownstreamContext context, OcelotRequestDelegate next)
{
await next.Invoke(context);
return context;
}
}
}

View File

@ -1,33 +1,16 @@
using Ocelot.Errors; namespace Ocelot.Middleware
using Ocelot.Logging;
using System.Collections.Generic;
namespace Ocelot.Middleware
{ {
using Ocelot.Logging;
public abstract class OcelotMiddleware public abstract class OcelotMiddleware
{ {
protected OcelotMiddleware(IOcelotLogger logger) protected OcelotMiddleware(IOcelotLogger logger)
{ {
Logger = logger; Logger = logger;
MiddlewareName = this.GetType().Name; MiddlewareName = GetType().Name;
} }
public IOcelotLogger Logger { get; } public IOcelotLogger Logger { get; }
public string MiddlewareName { get; } public string MiddlewareName { get; }
public void SetPipelineError(DownstreamContext context, List<Error> errors)
{
foreach (var error in errors)
{
SetPipelineError(context, error);
}
}
public void SetPipelineError(DownstreamContext context, Error error)
{
Logger.LogWarning(error.Message);
context.Errors.Add(error);
}
} }
} }

View File

@ -1,6 +1,6 @@
namespace Ocelot.Middleware namespace Ocelot.Middleware
{ {
using DependencyInjection; using Ocelot.DependencyInjection;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -11,7 +11,6 @@
using Ocelot.Configuration.Repository; using Ocelot.Configuration.Repository;
using Ocelot.Configuration.Setter; using Ocelot.Configuration.Setter;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.Middleware.Pipeline;
using Ocelot.Responses; using Ocelot.Responses;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
@ -42,37 +41,25 @@
return CreateOcelotPipeline(builder, pipelineConfiguration); return CreateOcelotPipeline(builder, pipelineConfiguration);
} }
public static Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder app, Action<IOcelotPipelineBuilder, OcelotPipelineConfiguration> builderAction) public static Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder app, Action<IApplicationBuilder, OcelotPipelineConfiguration> builderAction)
=> UseOcelot(app, builderAction, new OcelotPipelineConfiguration()); => UseOcelot(app, builderAction, new OcelotPipelineConfiguration());
public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder app, Action<IOcelotPipelineBuilder, OcelotPipelineConfiguration> builderAction, OcelotPipelineConfiguration configuration) public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder app, Action<IApplicationBuilder, OcelotPipelineConfiguration> builderAction, OcelotPipelineConfiguration configuration)
{ {
await CreateConfiguration(app); // initConfiguration await CreateConfiguration(app);
ConfigureDiagnosticListener(app); ConfigureDiagnosticListener(app);
var ocelotPipelineBuilder = new OcelotPipelineBuilder(app.ApplicationServices); builderAction?.Invoke(app, configuration ?? new OcelotPipelineConfiguration());
builderAction?.Invoke(ocelotPipelineBuilder, configuration ?? new OcelotPipelineConfiguration());
var ocelotDelegate = ocelotPipelineBuilder.Build();
app.Properties["analysis.NextMiddlewareName"] = "TransitionToOcelotMiddleware"; app.Properties["analysis.NextMiddlewareName"] = "TransitionToOcelotMiddleware";
app.Use(async (context, task) =>
{
var downstreamContext = new DownstreamContext(context);
await ocelotDelegate.Invoke(downstreamContext);
});
return app; return app;
} }
private static IApplicationBuilder CreateOcelotPipeline(IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration) private static IApplicationBuilder CreateOcelotPipeline(IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration)
{ {
var pipelineBuilder = new OcelotPipelineBuilder(builder.ApplicationServices); builder.BuildOcelotPipeline(pipelineConfiguration);
pipelineBuilder.BuildOcelotPipeline(pipelineConfiguration);
var firstDelegate = pipelineBuilder.Build();
/* /*
inject first delegate into first piece of asp.net middleware..maybe not like this inject first delegate into first piece of asp.net middleware..maybe not like this
@ -82,12 +69,6 @@
builder.Properties["analysis.NextMiddlewareName"] = "TransitionToOcelotMiddleware"; builder.Properties["analysis.NextMiddlewareName"] = "TransitionToOcelotMiddleware";
builder.Use(async (context, task) =>
{
var downstreamContext = new DownstreamContext(context);
await firstDelegate.Invoke(downstreamContext);
});
return builder; return builder;
} }

View File

@ -1,9 +1,10 @@
namespace Ocelot.Middleware namespace Ocelot.Middleware
{ {
using Ocelot.Middleware.Pipeline;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
public class OcelotPipelineConfiguration public class OcelotPipelineConfiguration
{ {
@ -12,38 +13,64 @@
/// is the next thing called in the Ocelot pipeline. Anything after next.invoke is the last thing called /// is the next thing called in the Ocelot pipeline. Anything after next.invoke is the last thing called
/// in the Ocelot pipeline before we go to the global error handler. /// in the Ocelot pipeline before we go to the global error handler.
/// </summary> /// </summary>
public Func<DownstreamContext, Func<Task>, Task> PreErrorResponderMiddleware { get; set; } /// <value>
/// <placeholder>This is called after the global error handling middleware so any code before calling next.invoke
/// is the next thing called in the Ocelot pipeline. Anything after next.invoke is the last thing called
/// in the Ocelot pipeline before we go to the global error handler.</placeholder>
/// </value>
public Func<HttpContext, Func<Task>, Task> PreErrorResponderMiddleware { get; set; }
/// <summary> /// <summary>
/// This is to allow the user to run any extra authentication before the Ocelot authentication /// This is to allow the user to run any extra authentication before the Ocelot authentication
/// kicks in /// kicks in
/// </summary> /// </summary>
public Func<DownstreamContext, Func<Task>, Task> PreAuthenticationMiddleware { get; set; } /// <value>
/// <placeholder>This is to allow the user to run any extra authentication before the Ocelot authentication
/// kicks in</placeholder>
/// </value>
public Func<HttpContext, Func<Task>, Task> PreAuthenticationMiddleware { get; set; }
/// <summary> /// <summary>
/// This allows the user to completely override the ocelot authentication middleware /// This allows the user to completely override the ocelot authentication middleware
/// </summary> /// </summary>
public Func<DownstreamContext, Func<Task>, Task> AuthenticationMiddleware { get; set; } /// <value>
/// <placeholder>This allows the user to completely override the ocelot authentication middleware</placeholder>
/// </value>
public Func<HttpContext, Func<Task>, Task> AuthenticationMiddleware { get; set; }
/// <summary> /// <summary>
/// This is to allow the user to run any extra authorisation before the Ocelot authentication /// This is to allow the user to run any extra authorisation before the Ocelot authentication
/// kicks in /// kicks in
/// </summary> /// </summary>
public Func<DownstreamContext, Func<Task>, Task> PreAuthorisationMiddleware { get; set; } /// <value>
/// <placeholder>This is to allow the user to run any extra authorisation before the Ocelot authentication
/// kicks in</placeholder>
/// </value>
public Func<HttpContext, Func<Task>, Task> PreAuthorisationMiddleware { get; set; }
/// <summary> /// <summary>
/// This allows the user to completely override the ocelot authorisation middleware /// This allows the user to completely override the ocelot authorisation middleware
/// </summary> /// </summary>
public Func<DownstreamContext, Func<Task>, Task> AuthorisationMiddleware { get; set; } /// <value>
/// <placeholder>This allows the user to completely override the ocelot authorisation middleware</placeholder>
/// </value>
public Func<HttpContext, Func<Task>, Task> AuthorisationMiddleware { get; set; }
/// <summary> /// <summary>
/// This allows the user to implement there own query string manipulation logic /// This allows the user to implement there own query string manipulation logic
/// </summary> /// </summary>
public Func<DownstreamContext, Func<Task>, Task> PreQueryStringBuilderMiddleware { get; set; } /// <value>
/// <placeholder>This allows the user to implement there own query string manipulation logic</placeholder>
/// </value>
public Func<HttpContext, Func<Task>, Task> PreQueryStringBuilderMiddleware { get; set; }
/// <summary> /// <summary>
/// This is an extension that will branch to different pipes /// This is an extension that will branch to different pipes
/// </summary> /// </summary>
public List<Func<IOcelotPipelineBuilder, Func<DownstreamContext, bool>>> MapWhenOcelotPipeline { get; } = new List<Func<IOcelotPipelineBuilder, Func<DownstreamContext, bool>>>(); /// <value>
/// <placeholder>This is an extension that will branch to different pipes</placeholder>
/// </value>
// todo fix this data structure
public Dictionary<Func<HttpContext, bool>, Action<IApplicationBuilder>> MapWhenOcelotPipeline { get; } = new Dictionary<Func<HttpContext, bool>, Action<IApplicationBuilder>>();
} }
} }

View File

@ -1,101 +1,112 @@
using Ocelot.Authentication.Middleware; namespace Ocelot.Middleware
using Ocelot.Authorisation.Middleware;
using Ocelot.Cache.Middleware;
using Ocelot.Claims.Middleware;
using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.DownstreamUrlCreator.Middleware;
using Ocelot.Errors.Middleware;
using Ocelot.Headers.Middleware;
using Ocelot.LoadBalancer.Middleware;
using Ocelot.PathManipulation.Middleware;
using Ocelot.QueryStrings.Middleware;
using Ocelot.RateLimit.Middleware;
using Ocelot.Request.Middleware;
using Ocelot.Requester.Middleware;
using Ocelot.RequestId.Middleware;
using Ocelot.Responder.Middleware;
using Ocelot.Security.Middleware;
using Ocelot.WebSockets.Middleware;
using System;
using System.Threading.Tasks;
namespace Ocelot.Middleware.Pipeline
{ {
using Ocelot.QueryStrings.Middleware;
using Ocelot.RateLimit.Middleware;
using Ocelot.Request.Middleware;
using Ocelot.Requester.Middleware;
using Ocelot.RequestId.Middleware;
using Ocelot.Responder.Middleware;
using Ocelot.Security.Middleware;
using Ocelot.Authentication.Middleware;
using Ocelot.Authorisation.Middleware;
using Ocelot.Cache.Middleware;
using Ocelot.Claims.Middleware;
using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.DownstreamUrlCreator.Middleware;
using Ocelot.Errors.Middleware;
using Ocelot.Headers.Middleware;
using Ocelot.LoadBalancer.Middleware;
using System;
using System.Threading.Tasks;
using Ocelot.DownstreamPathManipulation.Middleware;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Ocelot.WebSockets.Middleware;
using Ocelot.Multiplexer;
public static class OcelotPipelineExtensions public static class OcelotPipelineExtensions
{ {
public static OcelotRequestDelegate BuildOcelotPipeline(this IOcelotPipelineBuilder builder, public static RequestDelegate BuildOcelotPipeline(this IApplicationBuilder app,
OcelotPipelineConfiguration pipelineConfiguration) OcelotPipelineConfiguration pipelineConfiguration)
{ {
// this sets up the downstream context and gets the config
app.UseDownstreamContextMiddleware();
// This is registered to catch any global exceptions that are not handled // This is registered to catch any global exceptions that are not handled
// It also sets the Request Id if anything is set globally // It also sets the Request Id if anything is set globally
builder.UseExceptionHandlerMiddleware(); app.UseExceptionHandlerMiddleware();
// If the request is for websockets upgrade we fork into a different pipeline // If the request is for websockets upgrade we fork into a different pipeline
builder.MapWhen(context => context.HttpContext.WebSockets.IsWebSocketRequest, app.MapWhen(httpContext => httpContext.WebSockets.IsWebSocketRequest,
app => wenSocketsApp =>
{ {
app.UseDownstreamRouteFinderMiddleware(); wenSocketsApp.UseDownstreamRouteFinderMiddleware();
app.UseDownstreamRequestInitialiser(); wenSocketsApp.UseMultiplexingMiddleware();
app.UseLoadBalancingMiddleware(); wenSocketsApp.UseDownstreamRequestInitialiser();
app.UseDownstreamUrlCreatorMiddleware(); wenSocketsApp.UseLoadBalancingMiddleware();
app.UseWebSocketsProxyMiddleware(); wenSocketsApp.UseDownstreamUrlCreatorMiddleware();
wenSocketsApp.UseWebSocketsProxyMiddleware();
}); });
// Allow the user to respond with absolutely anything they want. // Allow the user to respond with absolutely anything they want.
builder.UseIfNotNull(pipelineConfiguration.PreErrorResponderMiddleware); app.UseIfNotNull(pipelineConfiguration.PreErrorResponderMiddleware);
// This is registered first so it can catch any errors and issue an appropriate response // This is registered first so it can catch any errors and issue an appropriate response
builder.UseResponderMiddleware(); app.UseResponderMiddleware();
// Then we get the downstream route information // Then we get the downstream route information
builder.UseDownstreamRouteFinderMiddleware(); app.UseDownstreamRouteFinderMiddleware();
// Multiplex the request if required
app.UseMultiplexingMiddleware();
// This security module, IP whitelist blacklist, extended security mechanism // This security module, IP whitelist blacklist, extended security mechanism
builder.UseSecurityMiddleware(); app.UseSecurityMiddleware();
//Expand other branch pipes //Expand other branch pipes
if (pipelineConfiguration.MapWhenOcelotPipeline != null) if (pipelineConfiguration.MapWhenOcelotPipeline != null)
{ {
foreach (var pipeline in pipelineConfiguration.MapWhenOcelotPipeline) foreach (var pipeline in pipelineConfiguration.MapWhenOcelotPipeline)
{ {
builder.MapWhen(pipeline); // todo why is this asking for an app app?
app.MapWhen(pipeline.Key, pipeline.Value);
} }
} }
// Now we have the ds route we can transform headers and stuff? // Now we have the ds route we can transform headers and stuff?
builder.UseHttpHeadersTransformationMiddleware(); app.UseHttpHeadersTransformationMiddleware();
// Initialises downstream request // Initialises downstream request
builder.UseDownstreamRequestInitialiser(); app.UseDownstreamRequestInitialiser();
// We check whether the request is ratelimit, and if there is no continue processing // We check whether the request is ratelimit, and if there is no continue processing
builder.UseRateLimiting(); app.UseRateLimiting();
// This adds or updates the request id (initally we try and set this based on global config in the error handling middleware) // This adds or updates the request id (initally we try and set this based on global config in the error handling middleware)
// If anything was set at global level and we have a different setting at re route level the global stuff will be overwritten // If anything was set at global level and we have a different setting at re route level the global stuff will be overwritten
// This means you can get a scenario where you have a different request id from the first piece of middleware to the request id middleware. // This means you can get a scenario where you have a different request id from the first piece of middleware to the request id middleware.
builder.UseRequestIdMiddleware(); app.UseRequestIdMiddleware();
// Allow pre authentication logic. The idea being people might want to run something custom before what is built in. // Allow pre authentication logic. The idea being people might want to run something custom before what is built in.
builder.UseIfNotNull(pipelineConfiguration.PreAuthenticationMiddleware); app.UseIfNotNull(pipelineConfiguration.PreAuthenticationMiddleware);
// Now we know where the client is going to go we can authenticate them. // Now we know where the client is going to go we can authenticate them.
// We allow the ocelot middleware to be overriden by whatever the // We allow the ocelot middleware to be overriden by whatever the
// user wants // user wants
if (pipelineConfiguration.AuthenticationMiddleware == null) if (pipelineConfiguration.AuthenticationMiddleware == null)
{ {
builder.UseAuthenticationMiddleware(); app.UseAuthenticationMiddleware();
} }
else else
{ {
builder.Use(pipelineConfiguration.AuthenticationMiddleware); app.Use(pipelineConfiguration.AuthenticationMiddleware);
} }
// The next thing we do is look at any claims transforms in case this is important for authorisation // The next thing we do is look at any claims transforms in case this is important for authorisation
builder.UseClaimsToClaimsMiddleware(); app.UseClaimsToClaimsMiddleware();
// Allow pre authorisation logic. The idea being people might want to run something custom before what is built in. // Allow pre authorisation logic. The idea being people might want to run something custom before what is built in.
builder.UseIfNotNull(pipelineConfiguration.PreAuthorisationMiddleware); app.UseIfNotNull(pipelineConfiguration.PreAuthorisationMiddleware);
// Now we have authenticated and done any claims transformation we // Now we have authenticated and done any claims transformation we
// can authorise the request // can authorise the request
@ -103,42 +114,42 @@ namespace Ocelot.Middleware.Pipeline
// user wants // user wants
if (pipelineConfiguration.AuthorisationMiddleware == null) if (pipelineConfiguration.AuthorisationMiddleware == null)
{ {
builder.UseAuthorisationMiddleware(); app.UseAuthorisationMiddleware();
} }
else else
{ {
builder.Use(pipelineConfiguration.AuthorisationMiddleware); app.Use(pipelineConfiguration.AuthorisationMiddleware);
} }
// Now we can run the claims to headers transformation middleware // Now we can run the claims to headers transformation middleware
builder.UseClaimsToHeadersMiddleware(); app.UseClaimsToHeadersMiddleware();
// Allow the user to implement their own query string manipulation logic // Allow the user to implement their own query string manipulation logic
builder.UseIfNotNull(pipelineConfiguration.PreQueryStringBuilderMiddleware); app.UseIfNotNull(pipelineConfiguration.PreQueryStringBuilderMiddleware);
// Now we can run any claims to query string transformation middleware // Now we can run any claims to query string transformation middleware
builder.UseClaimsToQueryStringMiddleware(); app.UseClaimsToQueryStringMiddleware();
builder.UseClaimsToDownstreamPathMiddleware(); app.UseClaimsToDownstreamPathMiddleware();
// Get the load balancer for this request // Get the load balancer for this request
builder.UseLoadBalancingMiddleware(); app.UseLoadBalancingMiddleware();
// This takes the downstream route we retrieved earlier and replaces any placeholders with the variables that should be used // This takes the downstream route we retrieved earlier and replaces any placeholders with the variables that should be used
builder.UseDownstreamUrlCreatorMiddleware(); app.UseDownstreamUrlCreatorMiddleware();
// Not sure if this is the best place for this but we use the downstream url // Not sure if this is the best place for this but we use the downstream url
// as the basis for our cache key. // as the basis for our cache key.
builder.UseOutputCacheMiddleware(); app.UseOutputCacheMiddleware();
//We fire off the request and set the response on the scoped data repo //We fire off the request and set the response on the scoped data repo
builder.UseHttpRequesterMiddleware(); app.UseHttpRequesterMiddleware();
return builder.Build(); return app.Build();
} }
private static void UseIfNotNull(this IOcelotPipelineBuilder builder, private static void UseIfNotNull(this IApplicationBuilder builder,
Func<DownstreamContext, Func<Task>, Task> middleware) Func<HttpContext, Func<Task>, Task> middleware)
{ {
if (middleware != null) if (middleware != null)
{ {

Some files were not shown because too many files have changed in this diff Show More