Skip to content

Commit

Permalink
Merge pull request #1961 from ThreeMammals/release/23.0
Browse files Browse the repository at this point in the history
Release 23.0
  • Loading branch information
raman-m authored Feb 13, 2024
2 parents 68e7127 + 290fbde commit e25d473
Show file tree
Hide file tree
Showing 111 changed files with 5,468 additions and 4,895 deletions.
73 changes: 58 additions & 15 deletions ReleaseNotes.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,63 @@
## Hotfix release (version {0})
> Default timeout vs the [Quality of Service](https://ocelot.readthedocs.io/en/latest/features/qualityofservice.html) feature
## November-December 2023 (version {0}) aka [Sunny Koliada](https://www.google.com/search?q=winter+solstice) release
> Codenamed as **[Sunny Koliada](https://www.bing.com/search?q=winter+solstice)**
Special thanks to **Alvin Huang**, @huanguolin!
### Focus On

### About
The bug is related to the **Quality of Service** feature (aka **QoS**) and the [HttpClient.Timeout](https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient.timeout) property.
- If JSON `QoSOptions` section is defined in the route config, then the bug is masked rather than active, and the timeout value is assigned from the [QoS TimeoutValue](https://ocelot.readthedocs.io/en/latest/features/qualityofservice.html#quality-of-service:~:text=%22TimeoutValue%22%3A%205000) property.
- If the `QoSOptions` section **is not** defined in the route config or the [TimeoutValue](https://ocelot.readthedocs.io/en/latest/features/qualityofservice.html#quality-of-service:~:text=%22TimeoutValue%22%3A%205000) property is missing, then the bug is **active** and affects downstream requests that **never time out**.
<details>
<summary><b>System performance</b>. System core performance review, redesign of system core related to routing and content streaming</summary>

### Technical info
In version [22.0](https://github.com/ThreeMammals/Ocelot/releases/tag/22.0.0), the bug was found in the explicit default constructor of the [FileQoSOptions](https://github.com/ThreeMammals/Ocelot/blob/main/src/Ocelot/Configuration/File/FileQoSOptions.cs) class with a maximum [TimeoutValue](https://github.com/ThreeMammals/Ocelot/blob/main/src/Ocelot/Configuration/File/FileQoSOptions.cs#L9). Previously, the default constructor was implicit with the default assignment of zero `0` to all `int` properties.
- Modification of the `RequestMapper` with a brand new `StreamHttpContent` class, in `Ocelot.Request.Mapper` namespace. The request body is no longer copied when it is handled by the API gateway, avoiding Out-of-Memory issues in pods/containers. This significantly reduces the gateway's memory consumption, and allows you to transfer content larger than 2 GB in streaming scenarios.
- Introduction of a new Message Invoker pool, in `Ocelot.Requester` namespace. We have replaced the [HttpClient](https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient) class with [HttpMessageInvoker](https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpmessageinvoker), which is the base class for `HttpClient`. The overall logic for managing the pool has been simplified, resulting in a reduction in the number of CPU cycles.
- Full HTTP content buffering is deactivated, resulting in a 50% reduction in memory consumption and a performance improvement of around 10%. Content is no longer copied on the API gateway, avoiding Out-of-Memory issues.
- **TODO** Include screenshots from Production...
</details>

The new explicit default constructor breaks the old implementation of [QoS TimeoutValue](https://github.com/ThreeMammals/Ocelot/blob/main/src/Ocelot/Requester/HttpClientBuilder.cs#L53-L55) logic, as our [QoS documentation](https://ocelot.readthedocs.io/en/latest/features/qualityofservice.html#quality-of-service:~:text=If%20you%20do%20not%20add%20a%20QoS%20section%2C%20QoS%20will%20not%20be%20used%2C%20however%20Ocelot%20will%20default%20to%20a%2090%20seconds%20timeout%20on%20all%20downstream%20requests.) states:
![image](https://github.com/ThreeMammals/Ocelot/assets/12430413/2c6b2cd3-e1c6-4510-9e46-883468c140ec) <br/>
**Finally**, the "default 90 second" logic for `HttpClient` breaks down when there are no **QoS** options and all requests on those routes are infinite, if, for example, downstream services are down or stuck.
<details>
<summary><b>Ocelot extra packages</b>. Total 3 Ocelot packs were updated</summary>

- [Ocelot.Cache.CacheManager](https://github.com/ThreeMammals/Ocelot/tree/main/src/Ocelot.Cache.CacheManager): Introduced default cache key generator with improved performance (the `DefaultCacheKeyGenerator` class). Old version of `CacheKeyGenerator` had significant performance issue when reading full content of HTTP request for caching key calculation of MD5 hash value. This hash value was excluded from the caching key.
- [Ocelot.Provider.Kubernetes](https://github.com/ThreeMammals/Ocelot/tree/main/src/Ocelot.Provider.Kubernetes): Fixed long lasting breaking change being added in version [15.0.0](https://github.com/ThreeMammals/Ocelot/releases/tag/15.0.0), see commit https://github.com/ThreeMammals/Ocelot/commit/6e5471a714dddb0a3a40fbb97eac2810cee1c78d. The bug persisted for more than 3 years in versions **15.0.0-22.0.1**, being masked multiple times via class renaming! **Special Thanks to @ZisisTsatsas** who once again brought this issue to our attention, and our team finally realized that we had a breaking change and the provider was broken.

#### The Bug Artifacts
- Reported bug issue: [1833](https://github.com/ThreeMammals/Ocelot/issues/1833) by @huanguolin
- Hotfix PR: [1834](https://github.com/ThreeMammals/Ocelot/pull/1834) by @huanguolin
- [Ocelot.Provider.Polly](https://github.com/ThreeMammals/Ocelot/tree/main/src/Ocelot.Provider.Polly): A minor changes without feature delivery. We are preparing for a major update to the package in the next release.
</details>

<details>
<summary><b>Middlewares</b>. Total 8 Ocelot middlewares were updated</summary>

- `AuthenticationMiddleware`: Added new [Multiple Authentication Schemes](https://github.com/ThreeMammals/Ocelot/pull/1870) feature by @MayorSheFF
- `OutputCacheMiddleware`, `RequestIdMiddleware`: Added new [Cache by Header Value](https://github.com/ThreeMammals/Ocelot/pull/1172) by @EngRajabi, and redesigned as [Default CacheKeyGenerator](https://github.com/ThreeMammals/Ocelot/pull/1849) feature by @raman-m
- `DownstreamUrlCreatorMiddleware`: Fixed [bug](https://github.com/ThreeMammals/Ocelot/issues/748) for ending/omitting slash in path templates aka [Empty placeholders](https://github.com/ThreeMammals/Ocelot/pull/1911) feature by @AlyHKafoury
- `ConfigurationMiddleware`, `HttpRequesterMiddleware`, `ResponderMiddleware`: System upgrade for [Custom HttpMessageInvoker pooling](https://github.com/ThreeMammals/Ocelot/pull/1824) feature by @ggnaegi
- `DownstreamRequestInitialiserMiddleware`: System upgrade for [Performance of Request Mapper](https://github.com/ThreeMammals/Ocelot/pull/1724) feature by @ggnaegi
</details>

<details>
<summary>Documentation for <b>Authentication</b>, <b>Caching</b>, <b>Kubernetes</b> and <b>Routing</b></summary>

- [Authentication](https://ocelot.readthedocs.io/en/latest/features/authentication.html)
- [Caching](https://ocelot.readthedocs.io/en/latest/features/caching.html)
- [Kubernetes](https://ocelot.readthedocs.io/en/latest/features/kubernetes.html)
- [Routing](https://ocelot.readthedocs.io/en/latest/features/routing.html)
</details>

<details>
<summary><b>Stabilization</b> aka bug fixing</summary>

- See [all bugs](https://github.com/ThreeMammals/Ocelot/issues?q=is%3Aissue+milestone%3ANov-December%2723+is%3Aclosed+label%3Abug) of the [Nov-December'23](https://github.com/ThreeMammals/Ocelot/milestone/2) milestone
</details>

<details>
<summary><b>Testing</b></summary>

- The `Ocelot.Benchmarks` testing project has been updated with new `PayloadBenchmarks` and `ResponseBenchmarks` by @ggnaegi
- The `Ocelot.AcceptanceTests` testing project has been refactored by @raman-m using the new `AuthenticationSteps` class, and more refactoring will be done in future releases
</details>

### Roadmap
We would like to share our team's plans for the future regarding: development trends, ideas, community expectations, etc.
- **Code Review and Performance Improvements**. Without a doubt, we care about code quality every day, following best development practices. And we review, test, refactor, and redesign features with overall performance in mind. In the next few releases (versions 23.x-24.0) we will take care of: generic providers, multiplexing middleware (Aggregation feature), memory management.
- **Server-Sent Events protocol support**. There is a lot of community interest in this HTTP-based protocol.
- **Long Polling for Consul provider**. [Consul](https://www.consul.io/) is our leading technology for service discovery. We are constantly improving the use cases for the `Ocelot.Provider.Consul` package and trying to improve the code inside the package.
- **QoS feature refactoring**. [Polly](https://github.com/App-vNext/Polly/) was released with the new v.8.2+ after .NET 8. So we have to update `Ocelot.Provider.Polly` package taking into account new Polly behavior of redesigned features.
- **Brainstorming** to redesign Rate Limiting, Websockets. More details in future release notes.
- **Planning** of support for Swagger and gRPC proto. More details in future release notes.
1 change: 1 addition & 0 deletions codeanalysis.ruleset
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<Rule Id="SA1005" Action="None" />
<Rule Id="SA1008" Action="None" />
<Rule Id="SA1009" Action="None" />
<Rule Id="SA1010" Action="None" />
<Rule Id="SA1011" Action="None" />
<Rule Id="SA1012" Action="None" />
<Rule Id="SA1013" Action="None" />
Expand Down
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information

project = 'Ocelot'
copyright = ' 2023 ThreeMammals Ocelot team'
copyright = ' 2016-2024 ThreeMammals Ocelot team'
author = 'Tom Pallister, Ocelot Core team at ThreeMammals'
release = '22.0'
release = '23.0'

# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
Expand Down
126 changes: 98 additions & 28 deletions docs/features/authentication.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,90 @@ Authentication
==============

In order to authenticate Routes and subsequently use any of Ocelot's claims based features such as authorization or modifying the request with values from the token,
users must register authentication services in their **Startup.cs** as usual but they provide a scheme (authentication provider key) with each registration e.g.
users must register authentication services in their **Startup.cs** as usual but they provide `a scheme <https://learn.microsoft.com/en-us/aspnet/core/security/authentication/#authentication-scheme>`_
(authentication provider key) with each registration e.g.

.. code-block:: csharp
public void ConfigureServices(IServiceCollection services)
{
var authenticationProviderKey = "TestKey";
const string AuthenticationProviderKey = "MyKey";
services
.AddAuthentication()
.AddJwtBearer(authenticationProviderKey,
options => { /* custom auth-setup */ });
.AddJwtBearer(AuthenticationProviderKey, options =>
{
// Custom Authentication setup via options initialization
});
}
In this example "**TestKey**" is the scheme that this provider has been registered with. We then map this to a Route in the configuration e.g.
In this example ``MyKey`` is `the scheme <https://learn.microsoft.com/en-us/aspnet/core/security/authentication/#authentication-scheme>`_ that this provider has been registered with.
We then map this to a Route in the configuration using the following `AuthenticationOptions <https://github.com/search?q=repo%3AThreeMammals%2FOcelot%20AuthenticationOptions&type=code>`_ properties:

* ``AuthenticationProviderKey`` is a string object, obsolete [#f1]_. This is legacy definition when you define :ref:`authentication-single`.
* ``AuthenticationProviderKeys`` is an array of strings, the recommended definition of :ref:`authentication-multiple` feature.

.. _authentication-single:

Single Key aka Authentication Scheme [#f1]_
-------------------------------------------

| Property: ``AuthenticationOptions.AuthenticationProviderKey``
We map authentication provider to a Route in the configuration e.g.

.. code-block:: json
"Routes": [{
"AuthenticationOptions": {
"AuthenticationProviderKey": "TestKey",
"AllowedScopes": []
}
}]
"AuthenticationOptions": {
"AuthenticationProviderKey": "MyKey",
"AllowedScopes": []
}
When Ocelot runs it will look at this Routes ``AuthenticationOptions.AuthenticationProviderKey`` and check that there is an authentication provider registered with the given key.
When Ocelot runs it will look at this Routes ``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 Route will use that provider when it executes.

If a Route 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 Unauthorized <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401>`_.

.. _authentication-multiple:

Multiple Authentication Schemes [#f2]_
--------------------------------------

| Property: ``AuthenticationOptions.AuthenticationProviderKeys``
In real world of ASP.NET, apps may need to support multiple types of authentication by single Ocelot app instance.
To register `multiple authentication schemes <https://learn.microsoft.com/en-us/aspnet/core/security/authorization/limitingidentitybyscheme#use-multiple-authentication-schemes>`_
(`authentication provider keys <https://github.com/search?q=repo%3AThreeMammals%2FOcelot%20AuthenticationProviderKey&type=code>`_) for each appropriate authentication provider, use and develop this abstract configuration of two or more schemes:

.. code-block:: csharp
public void ConfigureServices(IServiceCollection services)
{
const string DefaultScheme = JwtBearerDefaults.AuthenticationScheme; // Bearer
services.AddAuthentication()
.AddJwtBearer(DefaultScheme, options => { /* JWT setup */ })
// AddJwtBearer, AddCookie, AddIdentityServerAuthentication etc.
.AddMyProvider("MyKey", options => { /* Custom auth setup */ });
}
In this example, the ``MyKey`` and ``Bearer`` schemes represent the keys with which these providers were registered.
We then map these schemes to a Route in the configuration as shown below.

.. code-block:: json
"AuthenticationOptions": {
"AuthenticationProviderKeys": [ "Bearer", "MyKey" ] // The order matters!
"AllowedScopes": []
}
Afterward, Ocelot applies all steps that are specified for ``AuthenticationProviderKey`` as :ref:`authentication-single`.

**Note** that the order of the keys in an array definition does matter! We use a "First One Wins" authentication strategy.

Finally, we would say that registering providers, initializing options, forwarding authentication artifacts can be a "real" coding challenge.
If you're stuck or don't know what to do, just find inspiration in our `acceptance tests <https://github.com/search?q=repo%3AThreeMammals%2FOcelot+MultipleAuthSchemesFeatureTests+language%3AC%23&type=code&l=C%23>`_
(currently for `Identity Server 4 <https://identityserver4.readthedocs.io/>`_ only) [#f3]_.

JWT Tokens
----------

Expand All @@ -41,7 +95,7 @@ If you want to authenticate using JWT tokens maybe from a provider like `Auth0 <
public void ConfigureServices(IServiceCollection services)
{
var authenticationProviderKey = "TestKey";
var authenticationProviderKey = "MyKey";
services
.AddAuthentication()
.AddJwtBearer(authenticationProviderKey, options =>
Expand All @@ -56,12 +110,16 @@ Then map the authentication provider key to a Route in your configuration e.g.

.. code-block:: json
"Routes": [{
"AuthenticationOptions": {
"AuthenticationProviderKey": "TestKey",
"AllowedScopes": []
}
}]
"AuthenticationOptions": {
"AuthenticationProviderKeys": [ "MyKey" ],
"AllowedScopes": []
}
Docs
^^^^

* Microsoft Learn: `Authentication and authorization in minimal APIs <https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/security>`_
* Andrew Lock | .NET Escapades: `A look behind the JWT bearer authentication middleware in ASP.NET Core <https://andrewlock.net/a-look-behind-the-jwt-bearer-authentication-middleware-in-asp-net-core/>`_

Identity Server Bearer Tokens
-----------------------------
Expand All @@ -73,7 +131,7 @@ If you don't understand how to do this, please consult the IdentityServer `docum
public void ConfigureServices(IServiceCollection services)
{
var authenticationProviderKey = "TestKey";
var authenticationProviderKey = "MyKey";
Action<JwtBearerOptions> options = (opt) =>
{
opt.Authority = "https://whereyouridentityserverlives.com";
Expand All @@ -89,12 +147,10 @@ Then map the authentication provider key to a Route in your configuration e.g.

.. code-block:: json
"Routes": [{
"AuthenticationOptions": {
"AuthenticationProviderKey": "TestKey",
"AllowedScopes": []
}
}]
"AuthenticationOptions": {
"AuthenticationProviderKeys": [ "MyKey" ],
"AllowedScopes": []
}
Auth0 by Okta
-------------
Expand Down Expand Up @@ -137,8 +193,22 @@ If you add scopes to **AllowedScopes**, Ocelot will get all the user claims (fro

This is a way to restrict access to a Route on a per scope basis.

More identity providers
-----------------------
Links
-----

* Microsoft Learn: `Overview of ASP.NET Core authentication <https://learn.microsoft.com/en-us/aspnet/core/security/authentication/>`_
* Microsoft Learn: `Authorize with a specific scheme in ASP.NET Core <https://learn.microsoft.com/en-us/aspnet/core/security/authorization/limitingidentitybyscheme>`_
* Microsoft Learn: `Policy schemes in ASP.NET Core <https://learn.microsoft.com/en-us/aspnet/core/security/authentication/policyschemes>`_
* Microsoft .NET Blog: `ASP.NET Core Authentication with IdentityServer4 <https://devblogs.microsoft.com/dotnet/asp-net-core-authentication-with-identityserver4/>`_

Future
------

We invite you to add more examples, if you have integrated with other identity providers and the integration solution is working.
Please, open `Show and tell <https://github.com/ThreeMammals/Ocelot/discussions/categories/show-and-tell>`_ discussion in the repository.

""""

.. [#f1] Use the ``AuthenticationProviderKeys`` property instead of ``AuthenticationProviderKey`` one. We support this ``[Obsolete]`` property for backward compatibility and migration reasons. In future releases, the property may be removed as a breaking change.
.. [#f2] "`Multiple authentication schemes <https://learn.microsoft.com/en-us/aspnet/core/security/authorization/limitingidentitybyscheme#use-multiple-authentication-schemes>`__" feature was requested in issues `740 <https://github.com/ThreeMammals/Ocelot/issues/740>`_, `1580 <https://github.com/ThreeMammals/Ocelot/issues/1580>`_ and delivered as a part of `23.0 <https://github.com/ThreeMammals/Ocelot/releases/tag/23.0.0>`_ release.
.. [#f3] We would appreciate any new PRs to add extra acceptance tests for your custom scenarios with `multiple authentication schemes <https://learn.microsoft.com/en-us/aspnet/core/security/authorization/limitingidentitybyscheme#use-multiple-authentication-schemes>`__.
Loading

0 comments on commit e25d473

Please sign in to comment.