Skip to content

Commit

Permalink
Merge branch 'develop' into bug/1700-unstable-test-should-reload-conf…
Browse files Browse the repository at this point in the history
…ig-on-change
  • Loading branch information
raman-m authored Sep 25, 2023
2 parents 7461c6a + 75cd0b3 commit ea8156a
Show file tree
Hide file tree
Showing 20 changed files with 124 additions and 78 deletions.
28 changes: 17 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

# Ocelot

Ocelot is a .NET API Gateway. This project is aimed at people using .NET running a micro services / service oriented architecture
Ocelot is a .NET API Gateway. This project is aimed at people using .NET running a microservices / 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.

In particular I want easy integration with IdentityServer reference and bearer tokens.
Expand All @@ -15,7 +15,11 @@ We have been unable to find this in my current workplace without having to write

Ocelot is a bunch of middlewares in a specific order.

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!
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!

## Features

Expand All @@ -39,19 +43,21 @@ A quick list of Ocelot's capabilities for more information see the [documentatio
* Configuration / Administration REST API
* Platform / Cloud Agnostic

## How to install
## Install

Ocelot is designed to work with ASP.NET and it targets `net7.0`.

Install Ocelot and it's dependencies using NuGet.
Install Ocelot and its dependencies using NuGet Package Manager:
```powershell
Install-Package Ocelot
```

`Install-Package Ocelot`
Or via the .NET CLI:
```shell
dotnet add package Ocelot
```

Or via the .NET Core CLI:

`dotnet add package ocelot`

All versions can be found [here](https://www.nuget.org/packages/Ocelot/)
All versions can be found [here](https://www.nuget.org/packages/Ocelot/).

## Documentation

Expand All @@ -67,6 +73,6 @@ We love to receive contributions from the community so please keep them coming :

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 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 :)
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 in case 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 :)
7 changes: 4 additions & 3 deletions docs/building/building.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ Building

* You can also just run `dotnet tool restore && dotnet cake` locally!. Output will got to the `./artifacts` directory.

* The best way to replicate the CI process is to build Ocelot locally is using the Dockerfile.build file which can be found in the docker folder in Ocelot root. Use the following command `docker build --platform linux/amd64 -f ./docker/Dockerfile.build .` for example. You will need to change the platform flag depending on your platform.
* The best way to replicate the CI process is to build Ocelot locally is using the Dockerfile.build file which can be found in the docker folder in Ocelot root.
Use the following command ``docker build --platform linux/amd64 -f ./docker/Dockerfile.build .`` for example. You will need to change the platform flag depending on your platform.

* There is a Makefile to make it easier to call the various targers in `build.cake`. The scripts are called with .sh but can be easily changed to ps1 if you are using Windows.
* There is a Makefile to make it easier to call the various targets in `build.cake`. The scripts are called with **.sh** but can be easily changed to **.ps1** if you are using Windows.

* Alternatively you can build the project in VS2022 with the latest .NET 7.0 SDK.
* Alternatively you can build the project in VS2022 with the latest .NET 7.0 SDK.
2 changes: 1 addition & 1 deletion docs/features/authentication.rst
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,6 @@ NOTE: In order to get Ocelot to view the scope claim from Okta properly, you hav
Allowed Scopes
^^^^^^^^^^^^^

If you add scopes to AllowedScopes Ocelot will get all the user claims (from the token) of the type scope and make sure that the user has all of the scopes in the list.
If you add scopes to AllowedScopes Ocelot will get all the user claims (from the token) of the type scope and make sure that the user has at least one of the scopes in the list.

This is a way to restrict access to a Route on a per scope basis.
7 changes: 2 additions & 5 deletions docs/features/authorization.rst
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
Authorization
=============

Ocelot supports claims based authorization which is run post authentication. This means if you have a route you want to authorize you can add the following to you Route configuration.
Ocelot supports claims based authorization which is run post authentication. This means if you have a route you want to authorize you can add the following to your Route configuration.

.. code-block:: json
"RouteClaimsRequirement": {
"UserType": "registered"
}
In this example when the authorization 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 authorized and the response will be 403 forbidden.



In this example when the authorization middleware is called Ocelot will check to see 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 authorized and the response will be 403 forbidden.
4 changes: 3 additions & 1 deletion docs/features/caching.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Caching
=======

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.
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 recommend using this package to cache with Ocelot.

The following example shows how to add CacheManager to Ocelot so that you can do output caching.

Expand All @@ -15,6 +15,8 @@ The second thing you need to do something like the following to your ConfigureSe

.. code-block:: csharp
using Ocelot.Cache.CacheManager;
s.AddOcelot()
.AddCacheManager(x =>
{
Expand Down
17 changes: 9 additions & 8 deletions docs/features/claimstransformation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,21 @@ Claims Transformation

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.

After the user is authenticated we run the claims to claims transformation middleware. This allows the user to transform claims before the authorization middleware is called. After the user is authorized first we call the claims to headers middleware, thenthe claims to query string parameters middleware, and Finally the claims to downstream pathmiddleware.
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 authorized, 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 Route configuration a json dictionary is added with a specific name either AddClaimsToRequest, AddHeadersToRequest, AddQueriesToRequest, or ChangeDownstreamPathTemplate.
The syntax for performing the transforms is the same for each process. In the Route 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...

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 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.
The value of the entry is parsed to logic that will perform the transform. First of 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 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.

Claims to Claims Transformation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Below is an example configuration that will transforms claims to claims
Below is an example configuration that will transform claims to claims

.. code-block:: json
Expand All @@ -30,7 +31,7 @@ This shows a transforms where Ocelot looks at the users sub claim and transforms
Claims to Headers Tranformation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Below is an example configuration that will transforms claims to headers
Below is an example configuration that will transform claims to headers

.. code-block:: json
Expand All @@ -43,7 +44,7 @@ This shows a transform where Ocelot looks at the users sub claim and transforms
Claims to Query String Parameters Transformation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Below is an example configuration that will transforms claims to query string parameters
Below is an example configuration that will transform claims to query string parameters

.. code-block:: json
Expand All @@ -56,7 +57,7 @@ This shows a transform where Ocelot looks at the users LocationId claim and add
Claims to Downstream Path Transformation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Below is an example configuration that will transform claims to downstream path custom placeholders
Below is an example configuration that will transform claims to downstream path custom placeholders:

.. code-block:: json
Expand All @@ -69,4 +70,4 @@ Below is an example configuration that will transform claims to downstream path
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
the 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.
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.
6 changes: 5 additions & 1 deletion docs/features/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ Here is an example Route configuration, You don't need to set all of these thing
"TtlSeconds": 0,
"Region": ""
},
"SecurityOptions": {
"IPAllowedList": [],
"IPBlockedList": []
},
"RouteIsCaseSensitive": false,
"ServiceName": "",
"DownstreamScheme": "http",
Expand Down Expand Up @@ -139,7 +143,7 @@ Store configuration in Consul
-----------------------------

The first thing you need to do is install the NuGet package that provides Consul support in Ocelot.


.. code-block:: powershell
Install-Package Ocelot.Provider.Consul
Expand Down
26 changes: 16 additions & 10 deletions docs/features/errorcodes.rst
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
Http Error Status Codes
HTTP Error Status Codes
=======================

Ocelot will return HTTP status error codes based on internal logic in certain siturations:
- 401 if the authentication middleware runs and the user is not authenticated.
- 403 if the authorization middleware runs and the user is unauthenticated, claim value not authroised, scope not authorized, user doesnt have required claim or cannot find claim.
- 503 if the downstream request times out.
- 499 if the request is cancelled by the client.
- 404 if unable to find a downstream route.
- 502 if unable to connect to downstream service.
- 500 if unable to complete the HTTP request downstream and the exception is not OperationCanceledException or HttpRequestException.
- 404 if Ocelot is unable to map an internal error code to a HTTP status code.
Ocelot will return HTTP status error codes based on internal logic in certain situations:

Client error responses
----------------------

- **401** - if the authentication middleware runs and the user is not authenticated.
- **403** - if the authorization middleware runs and the user is unauthenticated, claim value not authorized, scope not authorized, user doesn't have required claim, or cannot find claim.
- **404** - if unable to find a downstream route, or Ocelot is unable to map an internal error code to a HTTP status code.
- **499** - if the request is cancelled by the client.

Server error responses
----------------------

- **500** - if unable to complete the HTTP request to downstream service, and the exception is not ``OperationCanceledException`` or ``HttpRequestException``.
- **502** - if unable to connect to downstream service.
- **503** - if the downstream request times out.
6 changes: 3 additions & 3 deletions docs/features/headerstransformation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Ocelot allows the user to transform headers pre and post downstream request. At
Add to Request
^^^^^^^^^^^^^^

This feature was requestes in `GitHub #313 <https://github.com/ThreeMammals/Ocelot/issues/313>`_.
This feature was requested in `GitHub #313 <https://github.com/ThreeMammals/Ocelot/issues/313>`_.

If you want to add a header to your upstream request please add the following to a Route in your ocelot.json:

Expand Down Expand Up @@ -141,10 +141,10 @@ Ideally this feature would be able to support the fact that a header can have mu
.. code-block:: json
"DownstreamHeaderTransform": {
"Location": "[{one,one},{two,two}"
"Location": "[{one,one},{two,two}]"
},
"HttpHandlerOptions": {
"AllowAutoRedirect": false,
},
If anyone wants to have a go at this please help yourself!!
If anyone wants to have a go at this please help yourself!
18 changes: 6 additions & 12 deletions docs/features/loadbalancer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ Please note that if you give more than one DownstreamHostAndPort or you are usin
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`_.
`David Lievrouw <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 setup to do load balancing.

Expand Down Expand Up @@ -135,37 +135,31 @@ Then you need to create a class that implements the ILoadBalancer interface. Bel

.. code-block:: csharp
private class CustomLoadBalancer : ILoadBalancer
public class CustomLoadBalancer : ILoadBalancer
{
private readonly Func<Task<List<Service>>> _services;
private readonly object _lock = new object();
private int _last;
public CustomLoadBalancer(Func<Task<List<Service>>> services)
{
_services = services;
}
public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext downstreamContext, HttpContext httpContext)
public async Task<Response<ServiceHostAndPort>> Lease(HttpContext httpContext)
{
var services = await _services();
lock (_lock)
{
if (_last >= services.Count)
{
_last = 0;
}
var next = services[_last];
_last++;
var next = services[_last++];
return new OkResponse<ServiceHostAndPort>(next.HostAndPort);
}
}
public void Release(ServiceHostAndPort hostAndPort)
{
}
public void Release(ServiceHostAndPort hostAndPort) { }
}
Finally you need to register this class with Ocelot. I have used the most complex example below to show all of the data / types that can be passed into the factory that creates load balancers.
Expand Down Expand Up @@ -207,4 +201,4 @@ There are numerous extension methods to add a custom load balancer and the inter
When you enable custom load balancers Ocelot looks up your load balancer by its class name when it decides if it should do load balancing. If it finds a match it will use your load balaner to load balance. If Ocelot cannot match the load balancer type in your configuration with the name of registered load balancer class then you will receive a HTTP 500 internal server error. If your load balancer factory throw an exception when Ocelot calls it you will receive a HTTP 500 internal server error.

Remember if you specify no load balancer in your config Ocelot will not try and load balance.
Remember if you specify no load balancer in your config Ocelot will not try and load balance.
Loading

0 comments on commit ea8156a

Please sign in to comment.