diff --git a/Ocelot.sln b/Ocelot.sln
index 1215130de..e40f83cfb 100644
--- a/Ocelot.sln
+++ b/Ocelot.sln
@@ -93,6 +93,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.ServiceDisco
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.ServiceDiscovery.DownstreamService", "samples\OcelotServiceDiscovery\DownstreamService\Ocelot.Samples.ServiceDiscovery.DownstreamService.csproj", "{E2AC741A-4120-4D59-B5E4-16382ED45E8D}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ocelot.Testing", "test\Ocelot.Testing\Ocelot.Testing.csproj", "{AE6BCCBD-0687-4C58-B30F-4ABBC6422087}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -203,6 +205,10 @@ Global
{E2AC741A-4120-4D59-B5E4-16382ED45E8D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E2AC741A-4120-4D59-B5E4-16382ED45E8D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E2AC741A-4120-4D59-B5E4-16382ED45E8D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AE6BCCBD-0687-4C58-B30F-4ABBC6422087}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AE6BCCBD-0687-4C58-B30F-4ABBC6422087}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AE6BCCBD-0687-4C58-B30F-4ABBC6422087}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AE6BCCBD-0687-4C58-B30F-4ABBC6422087}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -242,6 +248,7 @@ Global
{25C30AAA-12DD-4BA5-A53F-9271E54EBAB7} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
{D37209EA-C13E-42AE-B851-A8604F1FCD0E} = {25C30AAA-12DD-4BA5-A53F-9271E54EBAB7}
{E2AC741A-4120-4D59-B5E4-16382ED45E8D} = {25C30AAA-12DD-4BA5-A53F-9271E54EBAB7}
+ {AE6BCCBD-0687-4C58-B30F-4ABBC6422087} = {5B401523-36DA-4491-B73A-7590A26E420B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {21476EFF-778A-4F97-8A56-D1AF1CEC0C48}
diff --git a/ReleaseNotes.md b/ReleaseNotes.md
index 1193d5db1..e3df12c17 100644
--- a/ReleaseNotes.md
+++ b/ReleaseNotes.md
@@ -1,17 +1,33 @@
-## Upgrade to .NET 8 (version {0}) aka [.NET 8](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) release
-> Read article: [Announcing .NET 8](https://devblogs.microsoft.com/dotnet/announcing-dotnet-8/) by Gaurav Seth, on November 14th, 2023
+## October 2023 (version {0}) aka [Swiss Locomotive](https://en.wikipedia.org/wiki/SBB-CFF-FFS_Ae_6/6) release
+> Codenamed as **[Swiss Locomotive](https://www.google.com/search?q=swiss+locomotive)**
-### About
-We are pleased to announce to you that we can now offer the support of [.NET 8](https://dotnet.microsoft.com/en-us/download).
-But that is not all, in this release, we are adopting support of several versions of the .NET framework through [multitargeting](https://learn.microsoft.com/en-us/dotnet/standard/frameworks).
-The Ocelot distribution is now compatible with .NET **6**, **7** and **8**. :tada:
+### Focused On
+
+ Logging feature. Performance review, redesign and improvements with new best practices to log
-In the future, we will try to ensure the support of the [.NET SDKs](https://dotnet.microsoft.com/en-us/download/dotnet) that are still actively maintained by the .NET team and community.
-Current .NET versions in support are the following: [6, 7, 8](https://dotnet.microsoft.com/en-us/download/dotnet).
+ - Proposing a centralized `WriteLog` method for the `OcelotLogger`
+ - Factory methods for computed strings such as `string.Format` or interpolated strings
+ - Using `ILogger.IsEnabled` before calling the native `WriteLog` implementation and invoking string factory method
+
+
+ Quality of Service feature. Redesign and stabilization, and it produces less log records now.
+
+ - Fixing issue with [Polly](https://www.thepollyproject.org/) Circuit Breaker not opening after max number of retries reached
+ - Removing useless log calls that could have an impact on performance
+ - Polly [lib](https://www.nuget.org/packages/Polly#versions-body-tab) reference updating to latest `8.2.0` with some code improvements
+
+
+ Documentation for Logging, Request ID, Routing and Websockets
+
+ - [Logging](https://ocelot.readthedocs.io/en/latest/features/logging.html)
+ - [Request ID](https://ocelot.readthedocs.io/en/latest/features/requestid.html)
+ - [Routing](https://ocelot.readthedocs.io/en/latest/features/routing.html)
+ - [Websockets](https://ocelot.readthedocs.io/en/latest/features/websockets.html)
+
+
+ Testing improvements and stabilization aka bug fixing
-### Technical info
-As an ASP.NET Core app, now Ocelot targets `net6.0`, `net7.0` and `net8.0` frameworks.
-
-Starting with **v{0}**, the solution's code base supports [Multitargeting](https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-multitargeting-overview) as SDK-style projects.
-It should be easier for teams to move between (migrate to) .NET 6, 7 and 8 frameworks. Also, new features will be available for all .NET SDKs which we support via multitargeting.
-Find out more here: [Target frameworks in SDK-style projects](https://learn.microsoft.com/en-us/dotnet/standard/frameworks)
+ - [Routing](https://ocelot.readthedocs.io/en/latest/features/routing.html) bug fixing: query string placeholders including **CatchAll** one aka `{{everything}}` and query string duplicates removal
+ - [QoS](https://ocelot.readthedocs.io/en/latest/features/qualityofservice.html) bug fixing: Polly circuit breaker exceptions
+ - Testing bug fixing: rare failed builds because of unstable Polly tests. Acceptance common logic for ports
+
diff --git a/build.cake b/build.cake
index 75f30a755..1e8f4a032 100644
--- a/build.cake
+++ b/build.cake
@@ -161,7 +161,8 @@ Task("CreateReleaseNotes")
var releaseHeader = string.Format(System.IO.File.ReadAllText("./ReleaseNotes.md"), releaseVersion, lastRelease);
releaseNotes = new List { releaseHeader };
- var shortlogSummary = GitHelper($"shortlog --no-merges --numbered --summary {lastRelease}..HEAD");
+ var shortlogSummary = GitHelper($"shortlog --no-merges --numbered --summary {lastRelease}..HEAD")
+ .ToList();
var re = new Regex(@"^[\s\t]*(?'commits'\d+)[\s\t]+(?'author'.*)$");
var summary = shortlogSummary
.Where(x => re.IsMatch(x))
@@ -207,7 +208,6 @@ Task("CreateReleaseNotes")
static string HonorForDeletions(string place, string author, int commits, int files, int insertions, int deletions)
=> HonorForInsertions(place, author, commits, files, insertions, $"and **{deletions}** deletion{Plural(deletions)}");
- var statistics = new List<(string Contributor, int Files, int Insertions, int Deletions)>();
foreach (var group in commitsGrouping)
{
if (topContributors.Count >= top3) break;
@@ -220,6 +220,7 @@ Task("CreateReleaseNotes")
}
else // multiple candidates with the same number of commits, so, group by files changed
{
+ var statistics = new List<(string Contributor, int Files, int Insertions, int Deletions)>();
var shortstatRegex = new Regex(@"^\s*(?'files'\d+)\s+files?\s+changed(?'ins',\s+(?'insertions'\d+)\s+insertions?\(\+\))?(?'del',\s+(?'deletions'\d+)\s+deletions?\(\-\))?\s*$");
// Collect statistics from git log & shortlog
foreach (var author in group.authors)
@@ -315,15 +316,15 @@ private void WriteReleaseNotes()
Information($"RUN {nameof(WriteReleaseNotes)} ...");
EnsureDirectoryExists(packagesDir);
- System.IO.File.WriteAllLines(releaseNotesFile, releaseNotes);
+ System.IO.File.WriteAllLines(releaseNotesFile, releaseNotes, Encoding.UTF8);
- var content = System.IO.File.ReadAllText(releaseNotesFile);
+ var content = System.IO.File.ReadAllText(releaseNotesFile, Encoding.UTF8);
if (string.IsNullOrEmpty(content))
{
System.IO.File.WriteAllText(releaseNotesFile, "No commits since last release");
}
- Information($"Release notes are >>>\n{content}<<<");
+ Information("Release notes are >>>\n{0}<<<", content);
Information($"EXITED {nameof(WriteReleaseNotes)}");
}
@@ -510,10 +511,11 @@ Task("PublishToNuget")
.IsDependentOn("DownloadGitHubReleaseArtifacts")
.Does(() =>
{
- if (IsRunningOnCircleCI())
- {
- PublishPackages(packagesDir, artifactsFile, nugetFeedStableKey, nugetFeedStableUploadUrl, nugetFeedStableSymbolsUploadUrl);
- }
+ Information("Skipping of publishing to NuGet...");
+ // if (IsRunningOnCircleCI())
+ // {
+ // PublishPackages(packagesDir, artifactsFile, nugetFeedStableKey, nugetFeedStableUploadUrl, nugetFeedStableSymbolsUploadUrl);
+ // }
});
RunTarget(target);
diff --git a/docs/conf.py b/docs/conf.py
index 8bd28ecbf..dca77bb40 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -9,7 +9,7 @@
project = 'Ocelot'
copyright = ' 2023 ThreeMammals Ocelot team'
author = 'Tom Pallister, Ocelot Core team at ThreeMammals'
-release = '21.0'
+release = '22.0'
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
diff --git a/docs/features/caching.rst b/docs/features/caching.rst
index f557d1817..a7cd4096f 100644
--- a/docs/features/caching.rst
+++ b/docs/features/caching.rst
@@ -30,7 +30,7 @@ Finally, in order to use caching on a route in your Route configuration add this
.. code-block:: json
- "FileCacheOptions": { "TtlSeconds": 15, "Region": "somename" }
+ "FileCacheOptions": { "TtlSeconds": 15, "Region": "europe-central" }
In this example **TtlSeconds** is set to 15 which means the cache will expire after 15 seconds.
The **Region** represents a region of caching.
diff --git a/docs/features/configuration.rst b/docs/features/configuration.rst
index 589c4a0b0..936a78a9d 100644
--- a/docs/features/configuration.rst
+++ b/docs/features/configuration.rst
@@ -241,6 +241,8 @@ Use ``HttpHandlerOptions`` in a Route configuration to set up ``HttpHandler`` be
* **MaxConnectionsPerServer** This controls how many connections the internal ``HttpClient`` will open. This can be set at Route or global level.
+.. _ssl-errors:
+
SSL Errors
----------
diff --git a/docs/features/logging.rst b/docs/features/logging.rst
index 979b16145..037502db3 100644
--- a/docs/features/logging.rst
+++ b/docs/features/logging.rst
@@ -2,23 +2,158 @@ Logging
=======
Ocelot uses the standard logging interfaces ``ILoggerFactory`` and ``ILogger`` at the moment.
-This is encapsulated in ``IOcelotLogger`` and ``IOcelotLoggerFactory`` with an implementation for the standard `ASP.NET Core logging `_ stuff at the moment.
-This is because Ocelot adds some extra info to the logs such as **request ID** if it is configured.
+This is encapsulated in ``IOcelotLogger`` and ``IOcelotLoggerFactory`` with the implementation for the standard `ASP.NET Core logging `_ stuff at the moment.
+This is because Ocelot adds some extra info to the logs such as **RequestId** if it is configured.
There is a global `error handler middleware `_ 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.
+
+Request ID
+----------
The reason for not just using `bog standard `_ framework logging is that
-we could not work out how to override the request id that get's logged when setting **IncludeScopes** to ``true`` for logging settings.
+we could not work out how to override the **RequestId** that get's logged when setting **IncludeScopes** to ``true`` for logging settings.
Nicely onto the next feature.
+Every log record has these 2 properties:
+
+* **RequestId** represents ID of the current request as plain string, for example ``0HMVD33IIJRFR:00000001``
+* **PreviousRequestId** represents ID of the previous request
+
+As an ``IOcelotLogger`` interface object being injected to constructors of service classes, current default Ocelot logger (``OcelotLogger`` class) reads these 2 properties from the ``IRequestScopedDataRepository`` interface object.
+Find out more about these properties and other details on the *Request ID* logging feature in the :doc:`../features/requestid` chapter.
+
+.. _logging-warning:
+
Warning
-------
-If you are logging to `Console `_, you will get terrible performance.
-The team has had so many issues about performance issues with Ocelot and it is always logging level **Debug**, logging to `Console `_.
+If you are logging to MS `Console `_, you will get terrible performance.
+The team has had so many issues about performance issues with Ocelot and it is always logging level ``Debug``, logging to `Console `_.
* **Warning!** Make sure you are logging to something proper in production environment!
-* Use **Error** and **Critical** levels in production environment!
-* Use **Warning** level in testing environment!
+* Use ``Error`` and ``Critical`` levels in production environment!
+* Use ``Warning`` level in testing & staging environments!
+
+These and other recommendations are below in the :ref:`logging-best-practices` section.
+
+.. _logging-best-practices:
+
+Best Practices
+--------------
+
+ | Microsoft Learn сomplete reference: `Logging in .NET Core and ASP.NET Core `_
+
+Our recommendations to gain Ocelot best logging are the following.
+
+First
+^^^^^
+
+Ensure minimum level while `Configure logging `_.
+The minimum log level is set in the application's ``appsettings.json`` file. This level is defined in the **Logging** section, for example:
+
+.. code-block:: json
+
+ {
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+ }
+
+Whether using `Serilog `_ or the standard Microsoft providers, the logging configuration will be retrieved from this section.
+
+.. code-block:: csharp
+
+ .ConfigureAppConfiguration((_, config) =>
+ {
+ config.AddJsonFile($"appsettings.{env.EnvironmentName}.json", false, false);
+ // ...
+ })
+
+However, there is one thing to be aware of. It is possible to use the ``SetMinimumLevel()`` method to define the minimum logging level.
+Be careful and make sure you set the log level in one place only, like:
+
+.. code-block:: csharp
+
+ ConfigureLogging(logging =>
+ {
+ logging.ClearProviders();
+ logging.SetMinimumLevel(minLogLevel);
+ logging.AddConsole(); // MS Console for Development and/or Testing environments only
+ })
+
+Please also use the ``ClearProviders()`` method, so that only the providers you wish to use are taken into account, as in the example above, the console.
+
+Second
+^^^^^^
+
+Ensure proper usage of minimum logging level for each environment: development, testing, production, etc.
+So, once again, read important notes of the :ref:`logging-warning` section!
+
+Third
+^^^^^
+
+Ocelot's logging has been improved in `22.0 `_ version:
+it is now possible to use a factory method for message strings that will only be executed if the minimum log level allows it.
+
+For example, let's take a message containing information about several variables that should only be generated if the minimum log level is ``Debug``.
+If the minimum log level is ``Warning`` then the string is never generated.
+
+Therefore, when the string contains dynamic information aka ``string.Format``, or string value is generated by `string interpolation `_ expression,
+it is recommended to call the log method using anonymous delegate via an ``=>`` expression function:
+
+.. code-block:: csharp
+
+ Logger.LogDebug(() => $"downstream templates are {string.Join(", ", response.Data.Route.DownstreamRoute.Select(r => r.DownstreamPathTemplate.Value))}");
+
+otherwise a constant string is sufficient
+
+.. code-block:: csharp
+
+ Logger.LogDebug("My const string");
+
+Performance Review
+------------------
+
+Ocelot's logging performance has been improved in version `22.0 `__ (see PR `1745 `_).
+These changes were requested as part of issue `1744 `_ after team's `discussion `_.
+
+Top Logging Performance?
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+Here is a quick recipe for your Production environment!
+You need to ensure the minimal level is ``Critical`` or ``None``. Nothing more!
+For sure, having top logging performance means having less log records written by logging provider. So, logs should be pretty empty.
+
+Anyway, during the first time after a version release to production, we recommend to watch the system and current version app behavior by specifying ``Error`` minimum level.
+If release engineer will ensure stability of the version in production then minimum level can be increased to ``Critical`` or ``None`` to gain top performance.
+Technically this will switch off the logging feature at all.
+
+Run Benchmarks
+^^^^^^^^^^^^^^
+
+We have 2 types of benchmarks currently
+
+* ``SerilogBenchmarks`` with Serilog logging to a file. See ``ConfigureLogging`` method with ``logging.AddSerilog(_logger);``
+* ``MsLoggerBenchmarks`` with MS default logging to MS Console. See ``ConfigureLogging`` method with ``logging.AddConsole();``
+
+Benchmark results largely depend on the environment and hardware on which they run.
+We are pleased to invite you to run Logging benchmarks on your machine by the following instructions below.
+
+1. Open PowerShell or Command Prompt console
+2. Build Ocelot solution in Release mode: ``dotnet build --configuration Release``
+3. Go to ``test\Ocelot.Benchmarks\bin\Release\`` folder.
+4. Choose .NET version changing the folder, for example to ``net8.0``
+5. Run **Ocelot.Benchmarks.exe**: ``.\Ocelot.Benchmarks.exe``
+6. Run ``SerilogBenchmarks`` or ``MsLoggerBenchmarks`` by pressing appropriate number of a benchmark: ``5`` or ``6``, + Enter
+7. Wait for 3+ minutes to complete benchmark, and get final results.
+8. Read and analize your benchmark session results.
+
+Indicators
+^^^^^^^^^^
+
+``To be developed...``
diff --git a/docs/features/requestid.rst b/docs/features/requestid.rst
index 5fc46b936..014aaf3c5 100644
--- a/docs/features/requestid.rst
+++ b/docs/features/requestid.rst
@@ -1,13 +1,13 @@
Request ID
==========
- aka **Correlation ID**
+ aka **Correlation ID** or `HttpContext.TraceIdentifier `_
Ocelot supports a client sending a *request ID* in the form of a header.
-If set, Ocelot will 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.
+If set, Ocelot will use the **RequestId** for logging as soon as it becomes available in the middleware pipeline.
+Ocelot will also forward the *RequestId* with the specified header to the downstream service.
-You can still get the ASP.NET Core *request ID* in the logs if you set **IncludeScopes** ``true`` in your logging config.
+You can still get the ASP.NET Core *Request ID* in the logs if you set **IncludeScopes** ``true`` in your logging config.
In order to use the *Request ID* feature you have two options.
@@ -67,3 +67,24 @@ Below is an example of the logging when set at ``Debug`` level for a normal requ
requestId: 1234, previousRequestId: asdf, message: no pipeline errors, setting and returning completed response,
dbug: Ocelot.Errors.Middleware.ExceptionHandlerMiddleware[0]
requestId: 1234, previousRequestId: asdf, message: ocelot pipeline finished,
+
+And more practical example from secret production environment in Switzerland:
+
+.. code-block:: text
+
+ warn: Ocelot.DownstreamRouteFinder.Middleware.DownstreamRouteFinderMiddleware[0]
+ requestId: 0HMVD33IIJRFR:00000001, previousRequestId: no previous request id, message: DownstreamRouteFinderMiddleware setting pipeline errors. IDownstreamRouteFinder returned Error Code: UnableToFindDownstreamRouteError Message: Failed to match Route configuration for upstream path: /, verb: GET.
+ warn: Ocelot.Responder.Middleware.ResponderMiddleware[0]
+ requestId: 0HMVD33IIJRFR:00000001, previousRequestId: no previous request id, message: Error Code: UnableToFindDownstreamRouteError Message: Failed to match Route configuration for upstream path: /, verb: GET. errors found in ResponderMiddleware. Setting error response for request path:/, request method: GET
+
+Curious?
+--------
+
+*Request ID* is a part of big :doc:`../features/logging` feature.
+
+Every log record has these 2 properties:
+
+* **RequestId** represents ID of the current request as plain string, for example ``0HMVD33IIJRFR:00000001``
+* **PreviousRequestId** represents ID of the previous request
+
+As an ``IOcelotLogger`` interface object being injected to constructors of service classes, current default Ocelot logger (the ``OcelotLogger`` class) reads these 2 properties from the ``IRequestScopedDataRepository`` interface object.
diff --git a/docs/features/routing.rst b/docs/features/routing.rst
index 10c4ba1e3..e2ca941bf 100644
--- a/docs/features/routing.rst
+++ b/docs/features/routing.rst
@@ -169,42 +169,104 @@ This feature was requested in `issue 340 `_ Ocelot is able to forward query string parameters with their processing in the form of ``{something}``.
+Also, the query parameter placeholder needs to be present in both the **DownstreamPathTemplate** and **UpstreamPathTemplate** properties.
+Placeholder replacement works bi-directionally between path and query strings, with some `restrictions <#restrictions-on-use>`_ on usage.
+
+Path to Query String direction
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Ocelot allows you to specify a query string as part of the **DownstreamPathTemplate** like the example below:
.. code-block:: json
{
- "UpstreamHttpMethod": [ "Get" ],
- "UpstreamPathTemplate": "/api/units/{subscriptionId}/{unitId}/updates",
- "DownstreamPathTemplate": "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}",
- "DownstreamScheme": "http",
- "DownstreamHostAndPorts": [
- { "Host": "localhost", "Port": 50110 }
- ]
+ "UpstreamPathTemplate": "/api/units/{subscription}/{unit}/updates",
+ "DownstreamPathTemplate": "/api/subscriptions/{subscription}/updates?unitId={unit}",
}
-In this example Ocelot will use the value from the ``{unitId}`` placeholder in the upstream path template and add it to the downstream request as a query string parameter called ``unitId``!
+In this example Ocelot will use the value from the ``{unit}`` placeholder in the upstream path template and add it to the downstream request as a query string parameter called ``unitId``! Make sure you name the placeholder differently due to `restrictions <#restrictions-on-use>`_ on usage.
+
+
+Query String to Path direction
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Ocelot will also allow you to put query string parameters in the **UpstreamPathTemplate** so you can match certain queries to certain services:
.. code-block:: json
{
- "UpstreamHttpMethod": [ "Get" ],
- "UpstreamPathTemplate": "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}",
- "DownstreamPathTemplate": "/api/units/{subscriptionId}/{unitId}/updates",
- "DownstreamScheme": "http",
- "DownstreamHostAndPorts": [
- { "Host": "localhost", "Port": 50110 }
- ]
+ "UpstreamPathTemplate": "/api/subscriptions/{subscriptionId}/updates?unitId={uid}",
+ "DownstreamPathTemplate": "/api/units/{subscriptionId}/{uid}/updates",
}
In this example Ocelot will only match requests that have a matching URL path and the query string starts with ``unitId=something``.
You can have other queries after this but you must start with the matching parameter.
-Also Ocelot will swap the ``{unitId}`` parameter from the query string and use it in the downstream request path.
+Also Ocelot will swap the ``{uid}`` parameter from the query string and use it in the downstream request path.
+Note, the best practice is giving different placeholder name than the name of query parameter due to `restrictions <#restrictions-on-use>`_ on usage.
+
+Catch All Query String
+^^^^^^^^^^^^^^^^^^^^^^
+
+Ocelot's routing also supports a *Catch All* style routing to forward all query string parameters.
+The placeholder ``{everything}`` name does not matter, any name will work.
+
+.. code-block:: json
+
+ {
+ "UpstreamPathTemplate": "/contracts?{everything}",
+ "DownstreamPathTemplate": "/apipath/contracts?{everything}",
+ }
+
+This entire query string routing feature is very useful in cases where the query string should not be transformed but rather routed without any changes,
+such as OData filters and etc (see issue `1174 `_).
+
+Restrictions on use
+^^^^^^^^^^^^^^^^^^^
+
+The query string parameters are ordered and merged to produce the final downstream URL.
+This is necessary because the ``DownstreamUrlCreatorMiddleware`` needs to have some control when replacing placeholders and merging duplicate parameters.
+So, even if your parameter is presented as the first parameter in the upstream, then in the final downstream URL the said query parameter will have a different position.
+But this doesn't seem to break anything in the downstream API.
+
+Because of parameters merging, special ASP.NET API `model binding `_
+for arrays is not supported if you use array items representation like ``selectedCourses=1050&selectedCourses=2000``.
+This query string will be merged as ``selectedCourses=1050`` in downstream URL. So, array data will be lost!
+Make sure upstream clients generate correct query string for array models like ``selectedCourses[0]=1050&selectedCourses[1]=2000``.
+To understand array model bidings, see `Bind arrays and string values from headers and query strings `_ docs.
+
+**Warning!** Query string placeholders have naming restrictions due to ``DownstreamUrlCreatorMiddleware`` implementations.
+On the other hand, it gives you the flexibility to control whether the parameter is present in the final downstream URL.
+Here are two user scenarios.
+
+* User wants to save the parameter after replacing the placeholder (see issue `473 `_).
+ To do this you need to use the following template definition:
+
+ .. code-block:: json
+
+ {
+ "UpstreamPathTemplate": "/path/{serverId}/{action}",
+ "DownstreamPathTemplate": "/path2/{action}?server={serverId}"
+ }
+
+ So, ``{serverId}`` placeholder and ``server`` parameter **names are different**!
+ Finally, the ``server`` parameter is kept.
+
+* User wants to remove old parameter after replacing placeholder (see issue `952 `_).
+ To do this you need to use the same names:
+
+ .. code-block:: json
+
+ {
+ "UpstreamPathTemplate": "/users?userId={userId}",
+ "DownstreamPathTemplate": "/persons?personId={userId}"
+ }
+
+ So, both ``{userId}`` placeholder and ``userId`` parameter **names are the same**!
+ Finally, the ``userId`` parameter is removed.
Security Options
----------------
diff --git a/docs/features/websockets.rst b/docs/features/websockets.rst
index 5e1e4fc54..1501915d5 100644
--- a/docs/features/websockets.rst
+++ b/docs/features/websockets.rst
@@ -1,7 +1,8 @@
Websockets
==========
- `WebSockets Standard `_ by WHATWG organization
+ * `WebSockets Standard `_ by WHATWG organization
+ * `The WebSocket Protocol `_ by Internet Engineering Task Force (IETF) organization
Ocelot supports proxying `WebSockets `_ with some extra bits.
This functionality was requested in `issue 212 `_.
@@ -90,12 +91,56 @@ Note normal Ocelot routing rules apply the main thing is the scheme which is set
"UpstreamHttpMethod": [ "GET", "POST", "PUT", "DELETE", "OPTIONS" ],
"UpstreamPathTemplate": "/gateway/{catchAll}",
"DownstreamPathTemplate": "/{catchAll}",
- "DownstreamScheme": "ws",
+ "DownstreamScheme": "ws",
"DownstreamHostAndPorts": [
{ "Host": "localhost", "Port": 5001 }
]
}
+WebSocket Secure
+----------------
+
+If you define a route with Secured WebSocket protocol, use the ``wss`` scheme:
+
+.. code-block:: json
+
+ {
+ "DownstreamScheme": "wss",
+ // ...
+ }
+
+Keep in mind: you can use WebSocket SSL for both `SignalR <#signalr>`_ and `WebSockets <#websockets>`__.
+
+To understand ``wss`` scheme, browse to this:
+
+* Microsoft Learn: `Secure your connection with TLS/SSL `_
+* IETF | The WebSocket Protocol: `WebSocket URIs `_
+
+If you have questions, it may be helpful to search for documentation on MS Learn:
+
+* `Search for "secure websocket" `_
+
+SSL Errors
+^^^^^^^^^^
+
+If you want to ignore SSL warnings (errors), set the following in your Route config:
+
+.. code-block:: json
+
+ {
+ "DownstreamScheme": "wss",
+ "DangerousAcceptAnyServerCertificateValidator": true,
+ // ...
+ }
+
+**But we don't recommend doing this!** Read the official notes regarding :ref:`ssl-errors` in the :doc:`../features/configuration` doc,
+where you will also find best practices for your environments.
+
+**Note**, the ``wss`` scheme fake validator was added by `PR 1377 `_,
+as a part of issues `1375 `_, `1237 `_ and etc.
+This life hacking feature for self-signed SSL certificates is available in version `20.0 `_.
+It will be removed and/or reworked in future releases. See the :ref:`ssl-errors` section for details.
+
Supported
---------
diff --git a/docs/index.rst b/docs/index.rst
index 3ec283703..87d0afa5f 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1,4 +1,4 @@
-Welcome to Ocelot 21.0
+Welcome to Ocelot 22.0
======================
Thanks for taking a look at the Ocelot documentation! Please use the left hand navigation to get around.
diff --git a/src/Ocelot.Provider.Consul/Consul.cs b/src/Ocelot.Provider.Consul/Consul.cs
index 84bc7ceee..273fca2ab 100644
--- a/src/Ocelot.Provider.Consul/Consul.cs
+++ b/src/Ocelot.Provider.Consul/Consul.cs
@@ -44,7 +44,7 @@ public async Task> GetAsync()
else
{
_logger.LogWarning(
- $"Unable to use service address: '{service.Address}' and port: {service.Port} as it is invalid for the service: '{service.Service}'. Address must contain host only e.g. 'localhost', and port must be greater than 0.");
+ () => $"Unable to use service address: '{service.Address}' and port: {service.Port} as it is invalid for the service: '{service.Service}'. Address must contain host only e.g. 'localhost', and port must be greater than 0.");
}
}
diff --git a/src/Ocelot.Provider.Consul/PollConsul.cs b/src/Ocelot.Provider.Consul/PollConsul.cs
index 5806e60f1..45fd10b19 100644
--- a/src/Ocelot.Provider.Consul/PollConsul.cs
+++ b/src/Ocelot.Provider.Consul/PollConsul.cs
@@ -50,7 +50,7 @@ public Task> GetAsync()
try
{
- _logger.LogInformation($"Retrieving new client information for service: {ServiceName}...");
+ _logger.LogInformation(() => $"Retrieving new client information for service: {ServiceName}...");
_services = _consulServiceDiscoveryProvider.GetAsync().Result;
return Task.FromResult(_services);
}
diff --git a/src/Ocelot.Provider.Kubernetes/KubernetesServiceDiscoveryProvider.cs b/src/Ocelot.Provider.Kubernetes/KubernetesServiceDiscoveryProvider.cs
index ce3ca316f..92c7b99cb 100644
--- a/src/Ocelot.Provider.Kubernetes/KubernetesServiceDiscoveryProvider.cs
+++ b/src/Ocelot.Provider.Kubernetes/KubernetesServiceDiscoveryProvider.cs
@@ -30,7 +30,7 @@ public async Task> GetAsync()
}
else
{
- _logger.LogWarning($"namespace:{_kubeRegistryConfiguration.KubeNamespace}service:{_kubeRegistryConfiguration.KeyOfServiceInK8s} Unable to use ,it is invalid. Address must contain host only e.g. localhost and port must be greater than 0");
+ _logger.LogWarning(() => $"namespace:{_kubeRegistryConfiguration.KubeNamespace}service:{_kubeRegistryConfiguration.KeyOfServiceInK8s} Unable to use ,it is invalid. Address must contain host only e.g. localhost and port must be greater than 0");
}
return services;
diff --git a/src/Ocelot.Provider.Polly/CircuitBreaker.cs b/src/Ocelot.Provider.Polly/CircuitBreaker.cs
deleted file mode 100644
index ce2a89bf2..000000000
--- a/src/Ocelot.Provider.Polly/CircuitBreaker.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using Polly;
-
-namespace Ocelot.Provider.Polly
-{
- public class CircuitBreaker
- {
- private readonly List _policies = new();
-
- public CircuitBreaker(params IAsyncPolicy[] policies)
- {
- foreach (var policy in policies.Where(p => p != null))
- {
- _policies.Add(policy);
- }
- }
-
- public IAsyncPolicy[] Policies => _policies.ToArray();
- }
-}
diff --git a/src/Ocelot.Provider.Polly/Interfaces/IPollyQoSProvider.cs b/src/Ocelot.Provider.Polly/Interfaces/IPollyQoSProvider.cs
index 4b693ede0..214597531 100644
--- a/src/Ocelot.Provider.Polly/Interfaces/IPollyQoSProvider.cs
+++ b/src/Ocelot.Provider.Polly/Interfaces/IPollyQoSProvider.cs
@@ -1,6 +1,9 @@
-namespace Ocelot.Provider.Polly.Interfaces;
+using Ocelot.Configuration;
-public interface IPollyQoSProvider
+namespace Ocelot.Provider.Polly.Interfaces;
+
+public interface IPollyQoSProvider
+ where TResult : class
{
- CircuitBreaker CircuitBreaker { get; }
+ PollyPolicyWrapper GetPollyPolicyWrapper(DownstreamRoute route);
}
diff --git a/src/Ocelot.Provider.Polly/OcelotBuilderExtensions.cs b/src/Ocelot.Provider.Polly/OcelotBuilderExtensions.cs
index 0d167f571..c37c690d8 100644
--- a/src/Ocelot.Provider.Polly/OcelotBuilderExtensions.cs
+++ b/src/Ocelot.Provider.Polly/OcelotBuilderExtensions.cs
@@ -1,34 +1,43 @@
-using Microsoft.Extensions.DependencyInjection;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
using Ocelot.Configuration;
using Ocelot.DependencyInjection;
using Ocelot.Errors;
using Ocelot.Logging;
+using Ocelot.Provider.Polly.Interfaces;
using Ocelot.Requester;
using Polly.CircuitBreaker;
using Polly.Timeout;
-
-namespace Ocelot.Provider.Polly
-{
- public static class OcelotBuilderExtensions
- {
- public static IOcelotBuilder AddPolly(this IOcelotBuilder builder)
- {
- var errorMapping = new Dictionary>
- {
- {typeof(TaskCanceledException), e => new RequestTimedOutError(e)},
- {typeof(TimeoutRejectedException), e => new RequestTimedOutError(e)},
- {typeof(BrokenCircuitException), e => new RequestTimedOutError(e)},
- };
-
- builder.Services.AddSingleton(errorMapping);
-
- static DelegatingHandler QosDelegatingHandlerDelegate(DownstreamRoute route, IOcelotLoggerFactory logger)
- {
- return new PollyCircuitBreakingDelegatingHandler(new PollyQoSProvider(route, logger), logger);
- }
-
- builder.Services.AddSingleton((QosDelegatingHandlerDelegate)QosDelegatingHandlerDelegate);
- return builder;
- }
- }
+
+namespace Ocelot.Provider.Polly;
+
+public static class OcelotBuilderExtensions
+{
+ public static IOcelotBuilder AddPolly(this IOcelotBuilder builder,
+ QosDelegatingHandlerDelegate delegatingHandler,
+ Dictionary> errorMapping)
+ where T : class, IPollyQoSProvider
+ {
+ builder.Services
+ .AddSingleton(errorMapping)
+ .AddSingleton, T>()
+ .AddSingleton(delegatingHandler);
+
+ return builder;
+ }
+
+ public static IOcelotBuilder AddPolly(this IOcelotBuilder builder)
+ {
+ var errorMapping = new Dictionary>
+ {
+ { typeof(TaskCanceledException), e => new RequestTimedOutError(e) },
+ { typeof(TimeoutRejectedException), e => new RequestTimedOutError(e) },
+ { typeof(BrokenCircuitException), e => new RequestTimedOutError(e) },
+ { typeof(BrokenCircuitException), e => new RequestTimedOutError(e) },
+ };
+ return AddPolly(builder, GetDelegatingHandler, errorMapping);
+ }
+
+ private static DelegatingHandler GetDelegatingHandler(DownstreamRoute route, IHttpContextAccessor contextAccessor, IOcelotLoggerFactory loggerFactory)
+ => new PollyPoliciesDelegatingHandler(route, contextAccessor, loggerFactory);
}
diff --git a/src/Ocelot.Provider.Polly/PollyCircuitBreakingDelegatingHandler.cs b/src/Ocelot.Provider.Polly/PollyCircuitBreakingDelegatingHandler.cs
deleted file mode 100644
index 9153b70ce..000000000
--- a/src/Ocelot.Provider.Polly/PollyCircuitBreakingDelegatingHandler.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using Ocelot.Logging;
-using Ocelot.Provider.Polly.Interfaces;
-using Polly;
-using Polly.CircuitBreaker;
-
-namespace Ocelot.Provider.Polly
-{
- public class PollyCircuitBreakingDelegatingHandler : DelegatingHandler
- {
- private readonly IPollyQoSProvider _qoSProvider;
- private readonly IOcelotLogger _logger;
-
- public PollyCircuitBreakingDelegatingHandler(
- IPollyQoSProvider qoSProvider,
- IOcelotLoggerFactory loggerFactory)
- {
- _qoSProvider = qoSProvider;
- _logger = loggerFactory.CreateLogger();
- }
-
- protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
- {
- try
- {
- var policies = _qoSProvider.CircuitBreaker.Policies;
- if (!policies.Any())
- {
- return await base.SendAsync(request, cancellationToken);
- }
-
- IAsyncPolicy policy = policies.Length > 1
- ? Policy.WrapAsync(policies)
- : policies[0];
-
- return await policy.ExecuteAsync(() => base.SendAsync(request, cancellationToken));
- }
- catch (BrokenCircuitException ex)
- {
- _logger.LogError("Reached to allowed number of exceptions. Circuit is open", ex);
- throw;
- }
- catch (HttpRequestException ex)
- {
- _logger.LogError($"Error in {nameof(PollyCircuitBreakingDelegatingHandler)}.{nameof(SendAsync)}", ex);
- throw;
- }
- }
- }
-}
diff --git a/src/Ocelot.Provider.Polly/PollyPoliciesDelegatingHandler.cs b/src/Ocelot.Provider.Polly/PollyPoliciesDelegatingHandler.cs
new file mode 100644
index 000000000..03be86495
--- /dev/null
+++ b/src/Ocelot.Provider.Polly/PollyPoliciesDelegatingHandler.cs
@@ -0,0 +1,52 @@
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using Ocelot.Configuration;
+using Ocelot.Logging;
+using Ocelot.Provider.Polly.Interfaces;
+using Polly.CircuitBreaker;
+using System.Diagnostics;
+
+namespace Ocelot.Provider.Polly;
+
+public class PollyPoliciesDelegatingHandler : DelegatingHandler
+{
+ private readonly DownstreamRoute _route;
+ private readonly IHttpContextAccessor _contextAccessor;
+ private readonly IOcelotLogger _logger;
+
+ public PollyPoliciesDelegatingHandler(
+ DownstreamRoute route,
+ IHttpContextAccessor contextAccessor,
+ IOcelotLoggerFactory loggerFactory)
+ {
+ _route = route;
+ _contextAccessor = contextAccessor;
+ _logger = loggerFactory.CreateLogger();
+ }
+
+ private IPollyQoSProvider GetQoSProvider()
+ {
+ Debug.Assert(_contextAccessor.HttpContext != null, "_contextAccessor.HttpContext != null");
+ return _contextAccessor.HttpContext.RequestServices.GetService>();
+ }
+
+ ///
+ /// Sends an HTTP request to the inner handler to send to the server as an asynchronous operation.
+ ///
+ /// Downstream request.
+ /// Token to cancel the task.
+ /// A object of a result.
+ /// Exception thrown when a circuit is broken.
+ /// Exception thrown by and classes.
+ protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
+ {
+ var qoSProvider = GetQoSProvider();
+
+ // At least one policy (timeout) will be returned
+ // AsyncPollyPolicy can't be null
+ // AsyncPollyPolicy constructor will throw if no policy is provided
+ var policy = qoSProvider.GetPollyPolicyWrapper(_route).AsyncPollyPolicy;
+
+ return await policy.ExecuteAsync(async () => await base.SendAsync(request, cancellationToken));
+ }
+}
diff --git a/src/Ocelot.Provider.Polly/PollyPolicyWrapper.cs b/src/Ocelot.Provider.Polly/PollyPolicyWrapper.cs
new file mode 100644
index 000000000..0b3058cbb
--- /dev/null
+++ b/src/Ocelot.Provider.Polly/PollyPolicyWrapper.cs
@@ -0,0 +1,21 @@
+namespace Ocelot.Provider.Polly;
+
+public class PollyPolicyWrapper
+ where TResult : class
+{
+ ///
+ /// Initializes a new instance of the class.
+ /// We expect at least one policy to be passed in, default can't be null.
+ ///
+ /// The policies with at least a policy.
+ public PollyPolicyWrapper(params IAsyncPolicy[] policies)
+ {
+ var allPolicies = policies.Where(p => p != null).ToArray();
+
+ AsyncPollyPolicy = allPolicies.Length > 1 ?
+ Policy.WrapAsync(allPolicies) :
+ allPolicies[0];
+ }
+
+ public IAsyncPolicy AsyncPollyPolicy { get; }
+}
diff --git a/src/Ocelot.Provider.Polly/PollyQoSProvider.cs b/src/Ocelot.Provider.Polly/PollyQoSProvider.cs
index 420939de4..edd460e77 100644
--- a/src/Ocelot.Provider.Polly/PollyQoSProvider.cs
+++ b/src/Ocelot.Provider.Polly/PollyQoSProvider.cs
@@ -1,65 +1,78 @@
using Ocelot.Configuration;
using Ocelot.Logging;
using Ocelot.Provider.Polly.Interfaces;
-using Polly;
using Polly.CircuitBreaker;
using Polly.Timeout;
-
-namespace Ocelot.Provider.Polly
-{
- public class PollyQoSProvider : IPollyQoSProvider
- {
- private readonly AsyncCircuitBreakerPolicy _circuitBreakerPolicy;
- private readonly AsyncTimeoutPolicy _timeoutPolicy;
- private readonly IOcelotLogger _logger;
-
- public PollyQoSProvider(AsyncCircuitBreakerPolicy circuitBreakerPolicy, AsyncTimeoutPolicy timeoutPolicy, IOcelotLogger logger)
+using System.Net;
+
+namespace Ocelot.Provider.Polly;
+
+public class PollyQoSProvider : IPollyQoSProvider
+{
+ private readonly Dictionary> _policyWrappers = new();
+ private readonly object _lockObject = new();
+ private readonly IOcelotLogger _logger;
+
+ private readonly HashSet _serverErrorCodes = new()
+ {
+ HttpStatusCode.InternalServerError,
+ HttpStatusCode.NotImplemented,
+ HttpStatusCode.BadGateway,
+ HttpStatusCode.ServiceUnavailable,
+ HttpStatusCode.GatewayTimeout,
+ HttpStatusCode.HttpVersionNotSupported,
+ HttpStatusCode.VariantAlsoNegotiates,
+ HttpStatusCode.InsufficientStorage,
+ HttpStatusCode.LoopDetected,
+ };
+
+ public PollyQoSProvider(IOcelotLoggerFactory loggerFactory)
+ {
+ _logger = loggerFactory.CreateLogger();
+ }
+
+ private static string GetRouteName(DownstreamRoute route)
+ => string.IsNullOrWhiteSpace(route.ServiceName)
+ ? route.UpstreamPathTemplate?.Template ?? route.DownstreamPathTemplate?.Value ?? string.Empty
+ : route.ServiceName;
+
+ public PollyPolicyWrapper GetPollyPolicyWrapper(DownstreamRoute route)
+ {
+ lock (_lockObject)
{
- _circuitBreakerPolicy = circuitBreakerPolicy;
- _timeoutPolicy = timeoutPolicy;
- _logger = logger;
- }
-
- public PollyQoSProvider(DownstreamRoute route, IOcelotLoggerFactory loggerFactory)
- {
- _logger = loggerFactory.CreateLogger();
-
- _ = Enum.TryParse(route.QosOptions.TimeoutStrategy, out TimeoutStrategy strategy);
-
- _timeoutPolicy = Policy.TimeoutAsync(TimeSpan.FromMilliseconds(route.QosOptions.TimeoutValue), strategy);
-
- if (route.QosOptions.ExceptionsAllowedBeforeBreaking > 0)
- {
- _circuitBreakerPolicy = Policy
- .Handle()
- .Or()
- .Or()
- .CircuitBreakerAsync(
- exceptionsAllowedBeforeBreaking: route.QosOptions.ExceptionsAllowedBeforeBreaking,
- durationOfBreak: TimeSpan.FromMilliseconds(route.QosOptions.DurationOfBreak),
- onBreak: (ex, breakDelay) =>
- {
- _logger.LogError(
- ".Breaker logging: Breaking the circuit for " + breakDelay.TotalMilliseconds + "ms!", ex);
- },
- onReset: () =>
- {
- _logger.LogDebug(".Breaker logging: Call ok! Closed the circuit again.");
- },
- onHalfOpen: () =>
- {
- _logger.LogDebug(".Breaker logging: Half-open; next call is a trial.");
- }
- );
- }
- else
- {
- _circuitBreakerPolicy = null;
- }
-
- CircuitBreaker = new CircuitBreaker(_circuitBreakerPolicy, _timeoutPolicy);
- }
-
- public CircuitBreaker CircuitBreaker { get; }
- }
-}
+ var currentRouteName = GetRouteName(route);
+ if (!_policyWrappers.ContainsKey(currentRouteName))
+ {
+ _policyWrappers.Add(currentRouteName, PollyPolicyWrapperFactory(route));
+ }
+
+ return _policyWrappers[currentRouteName];
+ }
+ }
+
+ private PollyPolicyWrapper PollyPolicyWrapperFactory(DownstreamRoute route)
+ {
+ AsyncCircuitBreakerPolicy exceptionsAllowedBeforeBreakingPolicy = null;
+ if (route.QosOptions.ExceptionsAllowedBeforeBreaking > 0)
+ {
+ var info = $"Route: {GetRouteName(route)}; Breaker logging in {nameof(PollyQoSProvider)}: ";
+
+ exceptionsAllowedBeforeBreakingPolicy = Policy
+ .HandleResult(r => _serverErrorCodes.Contains(r.StatusCode))
+ .Or()
+ .Or()
+ .CircuitBreakerAsync(route.QosOptions.ExceptionsAllowedBeforeBreaking,
+ durationOfBreak: TimeSpan.FromMilliseconds(route.QosOptions.DurationOfBreak),
+ onBreak: (ex, breakDelay) => _logger.LogError(info + $"Breaking the circuit for {breakDelay.TotalMilliseconds} ms!", ex.Exception),
+ onReset: () => _logger.LogDebug(info + "Call OK! Closed the circuit again."),
+ onHalfOpen: () => _logger.LogDebug(info + "Half-open; Next call is a trial."));
+ }
+
+ var timeoutPolicy = Policy
+ .TimeoutAsync(
+ TimeSpan.FromMilliseconds(route.QosOptions.TimeoutValue),
+ TimeoutStrategy.Pessimistic);
+
+ return new PollyPolicyWrapper(exceptionsAllowedBeforeBreakingPolicy, timeoutPolicy);
+ }
+}
diff --git a/src/Ocelot.Provider.Polly/Usings.cs b/src/Ocelot.Provider.Polly/Usings.cs
index 0cc4c6d4f..fb5c12dc6 100644
--- a/src/Ocelot.Provider.Polly/Usings.cs
+++ b/src/Ocelot.Provider.Polly/Usings.cs
@@ -1,12 +1,10 @@
// Default Microsoft.NET.Sdk namespaces
global using System;
global using System.Collections.Generic;
-global using System.IO;
global using System.Linq;
global using System.Net.Http;
global using System.Threading;
global using System.Threading.Tasks;
// Project extra global namespaces
-global using Ocelot;
global using Polly;
diff --git a/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs b/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs
index 7e94118d2..843c3dac5 100644
--- a/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs
+++ b/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs
@@ -23,7 +23,7 @@ public async Task Invoke(HttpContext httpContext)
if (httpContext.Request.Method.ToUpper() != "OPTIONS" && IsAuthenticatedRoute(downstreamRoute))
{
- Logger.LogInformation($"{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 httpContext.AuthenticateAsync(downstreamRoute.AuthenticationOptions.AuthenticationProviderKey);
@@ -31,7 +31,7 @@ public async Task Invoke(HttpContext httpContext)
if (httpContext.User.Identity.IsAuthenticated)
{
- Logger.LogInformation($"Client has been authenticated for {httpContext.Request.Path}");
+ Logger.LogInformation(() => $"Client has been authenticated for {httpContext.Request.Path}");
await _next.Invoke(httpContext);
}
else
@@ -39,14 +39,14 @@ public async Task Invoke(HttpContext httpContext)
var error = new UnauthenticatedError(
$"Request for authenticated route {httpContext.Request.Path} by {httpContext.User.Identity.Name} was unauthenticated");
- Logger.LogWarning($"Client has NOT been authenticated for {httpContext.Request.Path} and pipeline error set. {error}");
+ Logger.LogWarning(() =>$"Client has NOT been authenticated for {httpContext.Request.Path} and pipeline error set. {error}");
httpContext.Items.SetError(error);
}
}
else
{
- Logger.LogInformation($"No authentication needed for {httpContext.Request.Path}");
+ Logger.LogInformation(() => $"No authentication needed for {httpContext.Request.Path}");
await _next.Invoke(httpContext);
}
diff --git a/src/Ocelot/Authorization/Middleware/AuthorizationMiddleware.cs b/src/Ocelot/Authorization/Middleware/AuthorizationMiddleware.cs
index 45c142631..84ccf526d 100644
--- a/src/Ocelot/Authorization/Middleware/AuthorizationMiddleware.cs
+++ b/src/Ocelot/Authorization/Middleware/AuthorizationMiddleware.cs
@@ -62,7 +62,7 @@ public async Task Invoke(HttpContext httpContext)
if (authorized.IsError)
{
- Logger.LogWarning($"Error whilst authorizing {httpContext.User.Identity.Name}. Setting pipeline error");
+ Logger.LogWarning(() => $"Error whilst authorizing {httpContext.User.Identity.Name}. Setting pipeline error");
httpContext.Items.UpsertErrors(authorized.Errors);
return;
@@ -70,19 +70,19 @@ public async Task Invoke(HttpContext httpContext)
if (IsAuthorized(authorized))
{
- Logger.LogInformation($"{httpContext.User.Identity.Name} has succesfully been authorized for {downstreamRoute.UpstreamPathTemplate.OriginalValue}.");
+ Logger.LogInformation(() => $"{httpContext.User.Identity.Name} has succesfully been authorized for {downstreamRoute.UpstreamPathTemplate.OriginalValue}.");
await _next.Invoke(httpContext);
}
else
{
- Logger.LogWarning($"{httpContext.User.Identity.Name} is not authorized to access {downstreamRoute.UpstreamPathTemplate.OriginalValue}. Setting pipeline error");
+ Logger.LogWarning(() => $"{httpContext.User.Identity.Name} is not authorized to access {downstreamRoute.UpstreamPathTemplate.OriginalValue}. Setting pipeline error");
httpContext.Items.SetError(new UnauthorizedError($"{httpContext.User.Identity.Name} is not authorized to access {downstreamRoute.UpstreamPathTemplate.OriginalValue}"));
}
}
else
{
- Logger.LogInformation($"{downstreamRoute.DownstreamPathTemplate.Value} route does not require user to be authorized");
+ Logger.LogInformation(() => $"{downstreamRoute.DownstreamPathTemplate.Value} route does not require user to be authorized");
await _next.Invoke(httpContext);
}
}
diff --git a/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs b/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs
index 0117c4536..5e3158b48 100644
--- a/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs
+++ b/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs
@@ -35,29 +35,29 @@ public async Task Invoke(HttpContext httpContext)
var downstreamUrlKey = $"{downstreamRequest.Method}-{downstreamRequest.OriginalString}";
var downStreamRequestCacheKey = _cacheGenerator.GenerateRequestCacheKey(downstreamRequest);
- Logger.LogDebug($"Started checking cache for the '{downstreamUrlKey}' key.");
+ Logger.LogDebug(() => $"Started checking cache for the '{downstreamUrlKey}' key.");
var cached = _outputCache.Get(downStreamRequestCacheKey, downstreamRoute.CacheOptions.Region);
if (cached != null)
{
- Logger.LogDebug($"Cache entry exists for the '{downstreamUrlKey}' key.");
+ Logger.LogDebug(() => $"Cache entry exists for the '{downstreamUrlKey}' key.");
var response = CreateHttpResponseMessage(cached);
SetHttpResponseMessageThisRequest(httpContext, response);
- Logger.LogDebug($"Finished returning of cached response for the '{downstreamUrlKey}' key.");
+ Logger.LogDebug(() => $"Finished returning of cached response for the '{downstreamUrlKey}' key.");
return;
}
- Logger.LogDebug($"No response cached for the '{downstreamUrlKey}' key.");
+ Logger.LogDebug(() => $"No response cached for the '{downstreamUrlKey}' key.");
await _next.Invoke(httpContext);
if (httpContext.Items.Errors().Count > 0)
{
- Logger.LogDebug($"There was a pipeline error for the '{downstreamUrlKey}' key.");
+ Logger.LogDebug(() => $"There was a pipeline error for the '{downstreamUrlKey}' key.");
return;
}
@@ -68,7 +68,7 @@ public async Task Invoke(HttpContext httpContext)
_outputCache.Add(downStreamRequestCacheKey, cached, TimeSpan.FromSeconds(downstreamRoute.CacheOptions.TtlSeconds), downstreamRoute.CacheOptions.Region);
- Logger.LogDebug($"Finished response added to cache for the '{downstreamUrlKey}' key.");
+ Logger.LogDebug(() => $"Finished response added to cache for the '{downstreamUrlKey}' key.");
}
private static void SetHttpResponseMessageThisRequest(HttpContext context,
diff --git a/src/Ocelot/Claims/Middleware/ClaimsToClaimsMiddleware.cs b/src/Ocelot/Claims/Middleware/ClaimsToClaimsMiddleware.cs
index 2b4db0309..8e82d5606 100644
--- a/src/Ocelot/Claims/Middleware/ClaimsToClaimsMiddleware.cs
+++ b/src/Ocelot/Claims/Middleware/ClaimsToClaimsMiddleware.cs
@@ -1,6 +1,6 @@
-using Microsoft.AspNetCore.Http;
-using Ocelot.Logging;
-using Ocelot.Middleware;
+using Microsoft.AspNetCore.Http;
+using Ocelot.Logging;
+using Ocelot.Middleware;
namespace Ocelot.Claims.Middleware
{
diff --git a/src/Ocelot/Configuration/Creator/ClaimsToThingCreator.cs b/src/Ocelot/Configuration/Creator/ClaimsToThingCreator.cs
index e9ff42f8a..c6c1da38e 100644
--- a/src/Ocelot/Configuration/Creator/ClaimsToThingCreator.cs
+++ b/src/Ocelot/Configuration/Creator/ClaimsToThingCreator.cs
@@ -25,7 +25,7 @@ public List Create(Dictionary inputToBeParsed)
if (claimToThing.IsError)
{
- _logger.LogDebug($"Unable to extract configuration for key: {input.Key} and value: {input.Value} your configuration file is incorrect");
+ _logger.LogDebug(() => $"Unable to extract configuration for key: {input.Key} and value: {input.Value} your configuration file is incorrect");
}
else
{
diff --git a/src/Ocelot/Configuration/Creator/HeaderFindAndReplaceCreator.cs b/src/Ocelot/Configuration/Creator/HeaderFindAndReplaceCreator.cs
index 3c53b543e..07c558fb0 100644
--- a/src/Ocelot/Configuration/Creator/HeaderFindAndReplaceCreator.cs
+++ b/src/Ocelot/Configuration/Creator/HeaderFindAndReplaceCreator.cs
@@ -32,7 +32,7 @@ public HeaderTransformations Create(FileRoute fileRoute)
}
else
{
- _logger.LogWarning($"Unable to add UpstreamHeaderTransform {input.Key}: {input.Value}");
+ _logger.LogWarning(() => $"Unable to add UpstreamHeaderTransform {input.Key}: {input.Value}");
}
}
else
@@ -55,7 +55,7 @@ public HeaderTransformations Create(FileRoute fileRoute)
}
else
{
- _logger.LogWarning($"Unable to add DownstreamHeaderTransform {input.Key}: {input.Value}");
+ _logger.LogWarning(() => $"Unable to add DownstreamHeaderTransform {input.Key}: {input.Value}");
}
}
else
diff --git a/src/Ocelot/Configuration/File/FileAuthenticationOptions.cs b/src/Ocelot/Configuration/File/FileAuthenticationOptions.cs
index afac5d922..eebce214c 100644
--- a/src/Ocelot/Configuration/File/FileAuthenticationOptions.cs
+++ b/src/Ocelot/Configuration/File/FileAuthenticationOptions.cs
@@ -6,6 +6,12 @@ public FileAuthenticationOptions()
{
AllowedScopes = new List();
}
+
+ public FileAuthenticationOptions(FileAuthenticationOptions from)
+ {
+ AllowedScopes = new(from.AllowedScopes);
+ AuthenticationProviderKey = from.AuthenticationProviderKey;
+ }
public string AuthenticationProviderKey { get; set; }
public List AllowedScopes { get; set; }
diff --git a/src/Ocelot/Configuration/File/FileCacheOptions.cs b/src/Ocelot/Configuration/File/FileCacheOptions.cs
index b5168b3c0..65c481344 100644
--- a/src/Ocelot/Configuration/File/FileCacheOptions.cs
+++ b/src/Ocelot/Configuration/File/FileCacheOptions.cs
@@ -2,7 +2,19 @@
{
public class FileCacheOptions
{
- public int TtlSeconds { get; set; }
+ public FileCacheOptions()
+ {
+ Region = string.Empty;
+ TtlSeconds = 0;
+ }
+
+ public FileCacheOptions(FileCacheOptions from)
+ {
+ Region = from.Region;
+ TtlSeconds = from.TtlSeconds;
+ }
+
public string Region { get; set; }
+ public int TtlSeconds { get; set; }
}
}
diff --git a/src/Ocelot/Configuration/File/FileHostAndPort.cs b/src/Ocelot/Configuration/File/FileHostAndPort.cs
index 23c745e09..a3eeaae83 100644
--- a/src/Ocelot/Configuration/File/FileHostAndPort.cs
+++ b/src/Ocelot/Configuration/File/FileHostAndPort.cs
@@ -1,7 +1,21 @@
namespace Ocelot.Configuration.File
{
public class FileHostAndPort
- {
+ {
+ public FileHostAndPort() { }
+
+ public FileHostAndPort(FileHostAndPort from)
+ {
+ Host = from.Host;
+ Port = from.Port;
+ }
+
+ public FileHostAndPort(string host, int port)
+ {
+ Host = host;
+ Port = port;
+ }
+
public string Host { get; set; }
public int Port { get; set; }
}
diff --git a/src/Ocelot/Configuration/File/FileHttpHandlerOptions.cs b/src/Ocelot/Configuration/File/FileHttpHandlerOptions.cs
index b83be428b..33e1a15cb 100644
--- a/src/Ocelot/Configuration/File/FileHttpHandlerOptions.cs
+++ b/src/Ocelot/Configuration/File/FileHttpHandlerOptions.cs
@@ -5,19 +5,23 @@ public class FileHttpHandlerOptions
public FileHttpHandlerOptions()
{
AllowAutoRedirect = false;
+ MaxConnectionsPerServer = int.MaxValue;
UseCookieContainer = false;
UseProxy = true;
- MaxConnectionsPerServer = int.MaxValue;
+ }
+
+ public FileHttpHandlerOptions(FileHttpHandlerOptions from)
+ {
+ AllowAutoRedirect = from.AllowAutoRedirect;
+ MaxConnectionsPerServer = from.MaxConnectionsPerServer;
+ UseCookieContainer = from.UseCookieContainer;
+ UseProxy = from.UseProxy;
}
public bool AllowAutoRedirect { get; set; }
-
+ public int MaxConnectionsPerServer { get; set; }
public bool UseCookieContainer { get; set; }
-
- public bool UseTracing { get; set; }
-
public bool UseProxy { get; set; }
-
- public int MaxConnectionsPerServer { get; set; }
+ public bool UseTracing { get; set; }
}
}
diff --git a/src/Ocelot/Configuration/File/FileLoadBalancerOptions.cs b/src/Ocelot/Configuration/File/FileLoadBalancerOptions.cs
index 105ccc7a5..45ea8a0a5 100644
--- a/src/Ocelot/Configuration/File/FileLoadBalancerOptions.cs
+++ b/src/Ocelot/Configuration/File/FileLoadBalancerOptions.cs
@@ -2,8 +2,22 @@ namespace Ocelot.Configuration.File
{
public class FileLoadBalancerOptions
{
- public string Type { get; set; }
- public string Key { get; set; }
+ public FileLoadBalancerOptions()
+ {
+ Expiry = int.MaxValue;
+ Key = string.Empty;
+ Type = string.Empty;
+ }
+
+ public FileLoadBalancerOptions(FileLoadBalancerOptions from)
+ {
+ Expiry = from.Expiry;
+ Key = from.Key;
+ Type = from.Type;
+ }
+
public int Expiry { get; set; }
+ public string Key { get; set; }
+ public string Type { get; set; }
}
}
diff --git a/src/Ocelot/Configuration/File/FileQoSOptions.cs b/src/Ocelot/Configuration/File/FileQoSOptions.cs
index 4e12b4a3a..85b51fa43 100644
--- a/src/Ocelot/Configuration/File/FileQoSOptions.cs
+++ b/src/Ocelot/Configuration/File/FileQoSOptions.cs
@@ -1,11 +1,30 @@
namespace Ocelot.Configuration.File
{
public class FileQoSOptions
- {
- public int ExceptionsAllowedBeforeBreaking { get; set; }
+ {
+ public FileQoSOptions()
+ {
+ DurationOfBreak = 1;
+ ExceptionsAllowedBeforeBreaking = 0;
+ TimeoutValue = int.MaxValue;
+ }
+
+ public FileQoSOptions(FileQoSOptions from)
+ {
+ DurationOfBreak = from.DurationOfBreak;
+ ExceptionsAllowedBeforeBreaking = from.ExceptionsAllowedBeforeBreaking;
+ TimeoutValue = from.TimeoutValue;
+ }
+
+ public FileQoSOptions(QoSOptions from)
+ {
+ DurationOfBreak = from.DurationOfBreak;
+ ExceptionsAllowedBeforeBreaking = from.ExceptionsAllowedBeforeBreaking;
+ TimeoutValue = from.TimeoutValue;
+ }
public int DurationOfBreak { get; set; }
-
+ public int ExceptionsAllowedBeforeBreaking { get; set; }
public int TimeoutValue { get; set; }
}
}
diff --git a/src/Ocelot/Configuration/File/FileRateLimitRule.cs b/src/Ocelot/Configuration/File/FileRateLimitRule.cs
index 907e5341c..ffbc0c994 100644
--- a/src/Ocelot/Configuration/File/FileRateLimitRule.cs
+++ b/src/Ocelot/Configuration/File/FileRateLimitRule.cs
@@ -7,6 +7,15 @@ public FileRateLimitRule()
ClientWhitelist = new List();
}
+ public FileRateLimitRule(FileRateLimitRule from)
+ {
+ ClientWhitelist = new(from.ClientWhitelist);
+ EnableRateLimiting = from.EnableRateLimiting;
+ Limit = from.Limit;
+ Period = from.Period;
+ PeriodTimespan = from.PeriodTimespan;
+ }
+
///
/// The list of allowed clients.
///
diff --git a/src/Ocelot/Configuration/File/FileRoute.cs b/src/Ocelot/Configuration/File/FileRoute.cs
index e99ed2bc1..5823113ad 100644
--- a/src/Ocelot/Configuration/File/FileRoute.cs
+++ b/src/Ocelot/Configuration/File/FileRoute.cs
@@ -1,59 +1,110 @@
namespace Ocelot.Configuration.File
{
- public class FileRoute : IRoute
+ public class FileRoute : IRoute, ICloneable
{
public FileRoute()
{
- UpstreamHttpMethod = new List();
- AddHeadersToRequest = new Dictionary();
AddClaimsToRequest = new Dictionary();
- RouteClaimsRequirement = new Dictionary();
+ AddHeadersToRequest = new Dictionary();
AddQueriesToRequest = new Dictionary();
+ AuthenticationOptions = new FileAuthenticationOptions();
ChangeDownstreamPathTemplate = new Dictionary();
+ DelegatingHandlers = new List();
DownstreamHeaderTransform = new Dictionary();
+ DownstreamHostAndPorts = new List();
FileCacheOptions = new FileCacheOptions();
- QoSOptions = new FileQoSOptions();
- RateLimitOptions = new FileRateLimitRule();
- AuthenticationOptions = new FileAuthenticationOptions();
HttpHandlerOptions = new FileHttpHandlerOptions();
- UpstreamHeaderTransform = new Dictionary();
- DownstreamHostAndPorts = new List();
- DelegatingHandlers = new List();
LoadBalancerOptions = new FileLoadBalancerOptions();
- SecurityOptions = new FileSecurityOptions();
Priority = 1;
- }
-
- public string DownstreamPathTemplate { get; set; }
- public string UpstreamPathTemplate { get; set; }
- public List UpstreamHttpMethod { get; set; }
- public string DownstreamHttpMethod { get; set; }
- public Dictionary AddHeadersToRequest { get; set; }
- public Dictionary UpstreamHeaderTransform { get; set; }
- public Dictionary DownstreamHeaderTransform { get; set; }
- public Dictionary AddClaimsToRequest { get; set; }
- public Dictionary RouteClaimsRequirement { get; set; }
- public Dictionary AddQueriesToRequest { get; set; }
- public Dictionary ChangeDownstreamPathTemplate { get; set; }
- public string RequestIdKey { get; set; }
- public FileCacheOptions FileCacheOptions { get; set; }
- public bool RouteIsCaseSensitive { get; set; }
- public string ServiceName { get; set; }
- public string ServiceNamespace { get; set; }
- public string DownstreamScheme { get; set; }
- public FileQoSOptions QoSOptions { get; set; }
- public FileLoadBalancerOptions LoadBalancerOptions { get; set; }
- public FileRateLimitRule RateLimitOptions { get; set; }
- public FileAuthenticationOptions AuthenticationOptions { get; set; }
- public FileHttpHandlerOptions HttpHandlerOptions { get; set; }
- public List DownstreamHostAndPorts { get; set; }
- public string UpstreamHost { get; set; }
- public string Key { get; set; }
- public List DelegatingHandlers { get; set; }
- public int Priority { get; set; }
- public int Timeout { get; set; }
- public bool DangerousAcceptAnyServerCertificateValidator { get; set; }
- public FileSecurityOptions SecurityOptions { get; set; }
- public string DownstreamHttpVersion { get; set; }
+ QoSOptions = new FileQoSOptions();
+ RateLimitOptions = new FileRateLimitRule();
+ RouteClaimsRequirement = new Dictionary();
+ SecurityOptions = new FileSecurityOptions();
+ UpstreamHeaderTransform = new Dictionary();
+ UpstreamHttpMethod = new List();
+ }
+
+ public FileRoute(FileRoute from)
+ {
+ DeepCopy(from, this);
+ }
+
+ public Dictionary AddClaimsToRequest { get; set; }
+ public Dictionary AddHeadersToRequest { get; set; }
+ public Dictionary AddQueriesToRequest { get; set; }
+ public FileAuthenticationOptions AuthenticationOptions { get; set; }
+ public Dictionary ChangeDownstreamPathTemplate { get; set; }
+ public bool DangerousAcceptAnyServerCertificateValidator { get; set; }
+ public List DelegatingHandlers { get; set; }
+ public Dictionary DownstreamHeaderTransform { get; set; }
+ public List DownstreamHostAndPorts { get; set; }
+ public string DownstreamHttpMethod { get; set; }
+ public string DownstreamHttpVersion { get; set; }
+ public string DownstreamPathTemplate { get; set; }
+ public string DownstreamScheme { get; set; }
+ public FileCacheOptions FileCacheOptions { get; set; }
+ public FileHttpHandlerOptions HttpHandlerOptions { get; set; }
+ public string Key { get; set; }
+ public FileLoadBalancerOptions LoadBalancerOptions { get; set; }
+ public int Priority { get; set; }
+ public FileQoSOptions QoSOptions { get; set; }
+ public FileRateLimitRule RateLimitOptions { get; set; }
+ public string RequestIdKey { get; set; }
+ public Dictionary RouteClaimsRequirement { get; set; }
+ public bool RouteIsCaseSensitive { get; set; }
+ public FileSecurityOptions SecurityOptions { get; set; }
+ public string ServiceName { get; set; }
+ public string ServiceNamespace { get; set; }
+ public int Timeout { get; set; }
+ public Dictionary UpstreamHeaderTransform { get; set; }
+ public string UpstreamHost { get; set; }
+ public List UpstreamHttpMethod { get; set; }
+ public string UpstreamPathTemplate { get; set; }
+
+ ///
+ /// Clones this object by making a deep copy.
+ ///
+ /// A deeply copied object.
+ public object Clone()
+ {
+ var other = (FileRoute)MemberwiseClone();
+ DeepCopy(this, other);
+ return other;
+ }
+
+ public static void DeepCopy(FileRoute from, FileRoute to)
+ {
+ to.AddClaimsToRequest = new(from.AddClaimsToRequest);
+ to.AddHeadersToRequest = new(from.AddHeadersToRequest);
+ to.AddQueriesToRequest = new(from.AddQueriesToRequest);
+ to.AuthenticationOptions = new(from.AuthenticationOptions);
+ to.ChangeDownstreamPathTemplate = new(from.ChangeDownstreamPathTemplate);
+ to.DangerousAcceptAnyServerCertificateValidator = from.DangerousAcceptAnyServerCertificateValidator;
+ to.DelegatingHandlers = new(from.DelegatingHandlers);
+ to.DownstreamHeaderTransform = new(from.DownstreamHeaderTransform);
+ to.DownstreamHostAndPorts = from.DownstreamHostAndPorts.Select(x => new FileHostAndPort(x)).ToList();
+ to.DownstreamHttpMethod = from.DownstreamHttpMethod;
+ to.DownstreamHttpVersion = from.DownstreamHttpVersion;
+ to.DownstreamPathTemplate = from.DownstreamPathTemplate;
+ to.DownstreamScheme = from.DownstreamScheme;
+ to.FileCacheOptions = new(from.FileCacheOptions);
+ to.HttpHandlerOptions = new(from.HttpHandlerOptions);
+ to.Key = from.Key;
+ to.LoadBalancerOptions = new(from.LoadBalancerOptions);
+ to.Priority = from.Priority;
+ to.QoSOptions = new(from.QoSOptions);
+ to.RateLimitOptions = new(from.RateLimitOptions);
+ to.RequestIdKey = from.RequestIdKey;
+ to.RouteClaimsRequirement = new(from.RouteClaimsRequirement);
+ to.RouteIsCaseSensitive = from.RouteIsCaseSensitive;
+ to.SecurityOptions = new(from.SecurityOptions);
+ to.ServiceName = from.ServiceName;
+ to.ServiceNamespace = from.ServiceNamespace;
+ to.Timeout = from.Timeout;
+ to.UpstreamHeaderTransform = new(from.UpstreamHeaderTransform);
+ to.UpstreamHost = from.UpstreamHost;
+ to.UpstreamHttpMethod = new(from.UpstreamHttpMethod);
+ to.UpstreamPathTemplate = from.UpstreamPathTemplate;
+ }
}
}
diff --git a/src/Ocelot/Configuration/File/FileSecurityOptions.cs b/src/Ocelot/Configuration/File/FileSecurityOptions.cs
index 77d762b11..ffd595cb4 100644
--- a/src/Ocelot/Configuration/File/FileSecurityOptions.cs
+++ b/src/Ocelot/Configuration/File/FileSecurityOptions.cs
@@ -9,6 +9,13 @@ public FileSecurityOptions()
ExcludeAllowedFromBlocked = false;
}
+ public FileSecurityOptions(FileSecurityOptions from)
+ {
+ IPAllowedList = new(from.IPAllowedList);
+ IPBlockedList = new(from.IPBlockedList);
+ ExcludeAllowedFromBlocked = from.ExcludeAllowedFromBlocked;
+ }
+
public FileSecurityOptions(string allowedIPs = null, string blockedIPs = null, bool? excludeAllowedFromBlocked = null)
: this()
{
diff --git a/src/Ocelot/Configuration/QoSOptions.cs b/src/Ocelot/Configuration/QoSOptions.cs
index 461ff292a..915f07e2a 100644
--- a/src/Ocelot/Configuration/QoSOptions.cs
+++ b/src/Ocelot/Configuration/QoSOptions.cs
@@ -1,31 +1,41 @@
-namespace Ocelot.Configuration
+using Ocelot.Configuration.File;
+
+namespace Ocelot.Configuration
{
public class QoSOptions
{
+ public QoSOptions(QoSOptions from)
+ {
+ DurationOfBreak = from.DurationOfBreak;
+ ExceptionsAllowedBeforeBreaking = from.ExceptionsAllowedBeforeBreaking;
+ Key = from.Key;
+ TimeoutValue = from.TimeoutValue;
+ }
+
+ public QoSOptions(FileQoSOptions from)
+ {
+ DurationOfBreak = from.DurationOfBreak;
+ ExceptionsAllowedBeforeBreaking = from.ExceptionsAllowedBeforeBreaking;
+ Key = string.Empty;
+ TimeoutValue = from.TimeoutValue;
+ }
+
public QoSOptions(
int exceptionsAllowedBeforeBreaking,
- int durationofBreak,
- int timeoutValue,
- string key,
- string timeoutStrategy = "Pessimistic")
+ int durationOfBreak,
+ int timeoutValue,
+ string key)
{
+ DurationOfBreak = durationOfBreak;
ExceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking;
- DurationOfBreak = durationofBreak;
- TimeoutValue = timeoutValue;
- TimeoutStrategy = timeoutStrategy;
Key = key;
+ TimeoutValue = timeoutValue;
}
- public int ExceptionsAllowedBeforeBreaking { get; }
-
public int DurationOfBreak { get; }
-
+ public int ExceptionsAllowedBeforeBreaking { get; }
+ public string Key { get; }
public int TimeoutValue { get; }
-
- public string TimeoutStrategy { get; }
-
public bool UseQos => ExceptionsAllowedBeforeBreaking > 0 || TimeoutValue > 0;
-
- public string Key { get; }
}
}
diff --git a/src/Ocelot/Configuration/Repository/FileConfigurationPoller.cs b/src/Ocelot/Configuration/Repository/FileConfigurationPoller.cs
index 61538a5d7..969a34161 100644
--- a/src/Ocelot/Configuration/Repository/FileConfigurationPoller.cs
+++ b/src/Ocelot/Configuration/Repository/FileConfigurationPoller.cs
@@ -68,7 +68,7 @@ private async Task Poll()
if (fileConfig.IsError)
{
- _logger.LogWarning($"error geting file config, errors are {string.Join(',', fileConfig.Errors.Select(x => x.Message))}");
+ _logger.LogWarning(() =>$"error geting file config, errors are {string.Join(',', fileConfig.Errors.Select(x => x.Message))}");
return;
}
diff --git a/src/Ocelot/DependencyInjection/OcelotBuilder.cs b/src/Ocelot/DependencyInjection/OcelotBuilder.cs
index 159d336ca..4d198711e 100644
--- a/src/Ocelot/DependencyInjection/OcelotBuilder.cs
+++ b/src/Ocelot/DependencyInjection/OcelotBuilder.cs
@@ -16,7 +16,7 @@
using Ocelot.Configuration.Validator;
using Ocelot.DownstreamRouteFinder.Finder;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
-using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
+using Ocelot.DownstreamUrlCreator;
using Ocelot.Headers;
using Ocelot.Infrastructure;
using Ocelot.Infrastructure.Claims.Parser;
@@ -91,7 +91,7 @@ public OcelotBuilder(IServiceCollection services, IConfiguration configurationRo
Services.AddSingleton();
Services.TryAddSingleton();
Services.TryAddSingleton();
- Services.TryAddSingleton();
+ Services.TryAddSingleton();
Services.TryAddSingleton();
Services.TryAddSingleton();
Services.TryAddSingleton();
@@ -103,7 +103,7 @@ public OcelotBuilder(IServiceCollection services, IConfiguration configurationRo
Services.TryAddSingleton();
Services.TryAddSingleton();
Services.TryAddSingleton();
- Services.TryAddSingleton();
+ Services.TryAddSingleton();
Services.AddSingleton();
Services.AddSingleton();
Services.TryAddSingleton();
diff --git a/src/Ocelot/DownstreamPathManipulation/Middleware/ClaimsToDownstreamPathMiddleware.cs b/src/Ocelot/DownstreamPathManipulation/Middleware/ClaimsToDownstreamPathMiddleware.cs
index 149a9ea09..e9c54c9dd 100644
--- a/src/Ocelot/DownstreamPathManipulation/Middleware/ClaimsToDownstreamPathMiddleware.cs
+++ b/src/Ocelot/DownstreamPathManipulation/Middleware/ClaimsToDownstreamPathMiddleware.cs
@@ -25,7 +25,7 @@ public async Task Invoke(HttpContext httpContext)
if (downstreamRoute.ClaimsToPath.Any())
{
- Logger.LogInformation($"{downstreamRoute.DownstreamPathTemplate.Value} has instructions to convert claims to path");
+ Logger.LogInformation(() => $"{downstreamRoute.DownstreamPathTemplate.Value} has instructions to convert claims to path");
var templatePlaceholderNameAndValues = httpContext.Items.TemplatePlaceholderNameAndValues();
diff --git a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteProviderFactory.cs b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteProviderFactory.cs
index b10ced14f..236941e4b 100644
--- a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteProviderFactory.cs
+++ b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteProviderFactory.cs
@@ -1,6 +1,6 @@
-using Microsoft.Extensions.DependencyInjection;
-using Ocelot.Configuration;
-using Ocelot.Logging;
+using Microsoft.Extensions.DependencyInjection;
+using Ocelot.Configuration;
+using Ocelot.Logging;
namespace Ocelot.DownstreamRouteFinder.Finder
{
@@ -22,6 +22,7 @@ public IDownstreamRouteProvider Get(IInternalConfiguration config)
if ((!config.Routes.Any() || config.Routes.All(x => string.IsNullOrEmpty(x.UpstreamTemplatePattern?.OriginalValue))) && IsServiceDiscovery(config.ServiceProviderConfiguration))
{
_logger.LogInformation($"Selected {nameof(DownstreamRouteCreator)} as DownstreamRouteProvider for this request");
+
return _providers[nameof(DownstreamRouteCreator)];
}
diff --git a/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddleware.cs b/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddleware.cs
index 678ed7ec2..63c21b76c 100644
--- a/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddleware.cs
+++ b/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddleware.cs
@@ -32,7 +32,7 @@ public async Task Invoke(HttpContext httpContext)
? hostHeader.Split(':')[0]
: hostHeader;
- Logger.LogDebug($"Upstream url path is {upstreamUrlPath}");
+ Logger.LogDebug(() => $"Upstream url path is {upstreamUrlPath}");
var internalConfiguration = httpContext.Items.IInternalConfiguration();
@@ -42,14 +42,13 @@ public async Task Invoke(HttpContext httpContext)
if (response.IsError)
{
- Logger.LogWarning($"{MiddlewareName} setting pipeline errors. IDownstreamRouteFinder returned {response.Errors.ToErrorString()}");
+ Logger.LogWarning(() => $"{MiddlewareName} setting pipeline errors. IDownstreamRouteFinder returned {response.Errors.ToErrorString()}");
httpContext.Items.UpsertErrors(response.Errors);
return;
}
- var downstreamPathTemplates = string.Join(", ", response.Data.Route.DownstreamRoute.Select(r => r.DownstreamPathTemplate.Value));
- Logger.LogDebug($"downstream templates are {downstreamPathTemplates}");
+ Logger.LogDebug(() => $"downstream templates are {string.Join(", ", response.Data.Route.DownstreamRoute.Select(r => r.DownstreamPathTemplate.Value))}");
// why set both of these on HttpContext
httpContext.Items.UpsertTemplatePlaceholderNameAndValues(response.Data.TemplatePlaceholderNameAndValues);
diff --git a/src/Ocelot/DownstreamUrlCreator/UrlTemplateReplacer/DownstreamTemplatePathPlaceholderReplacer.cs b/src/Ocelot/DownstreamUrlCreator/DownstreamPathPlaceholderReplacer.cs
similarity index 82%
rename from src/Ocelot/DownstreamUrlCreator/UrlTemplateReplacer/DownstreamTemplatePathPlaceholderReplacer.cs
rename to src/Ocelot/DownstreamUrlCreator/DownstreamPathPlaceholderReplacer.cs
index cebbfda76..11bbae38b 100644
--- a/src/Ocelot/DownstreamUrlCreator/UrlTemplateReplacer/DownstreamTemplatePathPlaceholderReplacer.cs
+++ b/src/Ocelot/DownstreamUrlCreator/DownstreamPathPlaceholderReplacer.cs
@@ -2,9 +2,9 @@
using Ocelot.Responses;
using Ocelot.Values;
-namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer
+namespace Ocelot.DownstreamUrlCreator
{
- public class DownstreamTemplatePathPlaceholderReplacer : IDownstreamPathPlaceholderReplacer
+ public class DownstreamPathPlaceholderReplacer : IDownstreamPathPlaceholderReplacer
{
public Response Replace(string downstreamPathTemplate,
List urlPathPlaceholderNameAndValues)
diff --git a/src/Ocelot/DownstreamUrlCreator/UrlTemplateReplacer/IDownstreamPathPlaceholderReplacer.cs b/src/Ocelot/DownstreamUrlCreator/IDownstreamPathPlaceholderReplacer.cs
similarity index 83%
rename from src/Ocelot/DownstreamUrlCreator/UrlTemplateReplacer/IDownstreamPathPlaceholderReplacer.cs
rename to src/Ocelot/DownstreamUrlCreator/IDownstreamPathPlaceholderReplacer.cs
index 6cbb83b02..edc657e72 100644
--- a/src/Ocelot/DownstreamUrlCreator/UrlTemplateReplacer/IDownstreamPathPlaceholderReplacer.cs
+++ b/src/Ocelot/DownstreamUrlCreator/IDownstreamPathPlaceholderReplacer.cs
@@ -2,7 +2,7 @@
using Ocelot.Responses;
using Ocelot.Values;
-namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer
+namespace Ocelot.DownstreamUrlCreator
{
public interface IDownstreamPathPlaceholderReplacer
{
diff --git a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs
index a9513c4af..f8ae6ed26 100644
--- a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs
+++ b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs
@@ -1,12 +1,12 @@
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
-using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
using Ocelot.Logging;
using Ocelot.Middleware;
using Ocelot.Request.Middleware;
using Ocelot.Responses;
using Ocelot.Values;
+using System.Web;
namespace Ocelot.DownstreamUrlCreator.Middleware
{
@@ -15,10 +15,15 @@ public class DownstreamUrlCreatorMiddleware : OcelotMiddleware
private readonly RequestDelegate _next;
private readonly IDownstreamPathPlaceholderReplacer _replacer;
- public DownstreamUrlCreatorMiddleware(RequestDelegate next,
+ private const char Ampersand = '&';
+ private const char QuestionMark = '?';
+ private const char OpeningBrace = '{';
+ private const char ClosingBrace = '}';
+
+ public DownstreamUrlCreatorMiddleware(
+ RequestDelegate next,
IOcelotLoggerFactory loggerFactory,
- IDownstreamPathPlaceholderReplacer replacer
- )
+ IDownstreamPathPlaceholderReplacer replacer)
: base(loggerFactory.CreateLogger())
{
_next = next;
@@ -28,17 +33,13 @@ IDownstreamPathPlaceholderReplacer replacer
public async Task Invoke(HttpContext httpContext)
{
var downstreamRoute = httpContext.Items.DownstreamRoute();
-
- var templatePlaceholderNameAndValues = httpContext.Items.TemplatePlaceholderNameAndValues();
-
- var response = _replacer
- .Replace(downstreamRoute.DownstreamPathTemplate.Value, templatePlaceholderNameAndValues);
-
+ var placeholders = httpContext.Items.TemplatePlaceholderNameAndValues();
+ var response = _replacer.Replace(downstreamRoute.DownstreamPathTemplate.Value, placeholders);
var downstreamRequest = httpContext.Items.DownstreamRequest();
if (response.IsError)
{
- Logger.LogDebug("IDownstreamPathPlaceholderReplacer returned an error, setting pipeline error");
+ Logger.LogDebug($"{nameof(IDownstreamPathPlaceholderReplacer)} returned an error, setting pipeline error");
httpContext.Items.UpsertErrors(response.Errors);
return;
@@ -54,7 +55,7 @@ public async Task Invoke(HttpContext httpContext)
if (ServiceFabricRequest(internalConfiguration, downstreamRoute))
{
- var (path, query) = CreateServiceFabricUri(downstreamRequest, downstreamRoute, templatePlaceholderNameAndValues, response);
+ var (path, query) = CreateServiceFabricUri(downstreamRequest, downstreamRoute, placeholders, response);
//todo check this works again hope there is a test..
downstreamRequest.AbsolutePath = path;
@@ -63,38 +64,56 @@ public async Task Invoke(HttpContext httpContext)
else
{
var dsPath = response.Data;
-
- if (ContainsQueryString(dsPath))
+ if (dsPath.Value.Contains(QuestionMark))
{
downstreamRequest.AbsolutePath = GetPath(dsPath);
-
- if (string.IsNullOrEmpty(downstreamRequest.Query))
- {
- downstreamRequest.Query = GetQueryString(dsPath);
- }
- else
- {
- downstreamRequest.Query += GetQueryString(dsPath).Replace('?', '&');
- }
+ var newQuery = GetQueryString(dsPath);
+ downstreamRequest.Query = string.IsNullOrEmpty(downstreamRequest.Query)
+ ? newQuery
+ : MergeQueryStringsWithoutDuplicateValues(downstreamRequest.Query, newQuery, placeholders);
}
else
{
- RemoveQueryStringParametersThatHaveBeenUsedInTemplate(downstreamRequest, templatePlaceholderNameAndValues);
+ RemoveQueryStringParametersThatHaveBeenUsedInTemplate(downstreamRequest, placeholders);
downstreamRequest.AbsolutePath = dsPath.Value;
}
}
- Logger.LogDebug($"Downstream url is {downstreamRequest}");
+ Logger.LogDebug(() => $"Downstream url is {downstreamRequest}");
await _next.Invoke(httpContext);
}
+ private static string MergeQueryStringsWithoutDuplicateValues(string queryString, string newQueryString, List placeholders)
+ {
+ newQueryString = newQueryString.Replace(QuestionMark, Ampersand);
+ var queries = HttpUtility.ParseQueryString(queryString);
+ var newQueries = HttpUtility.ParseQueryString(newQueryString);
+
+ var parameters = newQueries.AllKeys
+ .Where(key => !string.IsNullOrEmpty(key))
+ .ToDictionary(key => key, key => newQueries[key]);
+
+ _ = queries.AllKeys
+ .Where(key => !string.IsNullOrEmpty(key) && !parameters.ContainsKey(key))
+ .All(key => parameters.TryAdd(key, queries[key]));
+
+ // Remove old replaced query parameters
+ foreach (var placeholder in placeholders)
+ {
+ parameters.Remove(placeholder.Name.Trim(OpeningBrace, ClosingBrace));
+ }
+
+ var orderedParams = parameters.OrderBy(x => x.Key).Select(x => $"{x.Key}={x.Value}");
+ return QuestionMark + string.Join(Ampersand, orderedParams);
+ }
+
private static void RemoveQueryStringParametersThatHaveBeenUsedInTemplate(DownstreamRequest downstreamRequest, List templatePlaceholderNameAndValues)
{
foreach (var nAndV in templatePlaceholderNameAndValues)
{
- var name = nAndV.Name.Replace("{", string.Empty).Replace("}", string.Empty);
+ var name = nAndV.Name.Trim(OpeningBrace, ClosingBrace);
var rgx = new Regex($@"\b{name}={nAndV.Value}\b");
@@ -106,29 +125,24 @@ private static void RemoveQueryStringParametersThatHaveBeenUsedInTemplate(Downst
if (!string.IsNullOrEmpty(downstreamRequest.Query))
{
- downstreamRequest.Query = string.Concat("?", downstreamRequest.Query.AsSpan(1));
+ downstreamRequest.Query = QuestionMark + downstreamRequest.Query[1..];
}
}
- }
+ }
}
private static string GetPath(DownstreamPath dsPath)
{
- int length = dsPath.Value.IndexOf('?', StringComparison.Ordinal);
+ int length = dsPath.Value.IndexOf(QuestionMark, StringComparison.Ordinal);
return dsPath.Value[..length];
}
private static string GetQueryString(DownstreamPath dsPath)
{
- int startIndex = dsPath.Value.IndexOf('?', StringComparison.Ordinal);
+ int startIndex = dsPath.Value.IndexOf(QuestionMark, StringComparison.Ordinal);
return dsPath.Value[startIndex..];
}
- private static bool ContainsQueryString(DownstreamPath dsPath)
- {
- return dsPath.Value.Contains('?');
- }
-
private (string Path, string Query) CreateServiceFabricUri(DownstreamRequest downstreamRequest, DownstreamRoute downstreamRoute, List templatePlaceholderNameAndValues, Response dsPath)
{
var query = downstreamRequest.Query;
diff --git a/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddleware.cs b/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddleware.cs
index 0d2a961fb..cea6fd2d0 100644
--- a/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddleware.cs
+++ b/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddleware.cs
@@ -48,10 +48,7 @@ public async Task Invoke(HttpContext httpContext)
catch (Exception e)
{
Logger.LogDebug("error calling middleware");
-
- var message = CreateMessage(httpContext, e);
-
- Logger.LogError(message, e);
+ Logger.LogError(() => CreateMessage(httpContext, e), e);
SetInternalServerErrorOnResponse(httpContext);
}
diff --git a/src/Ocelot/Headers/AddHeadersToRequest.cs b/src/Ocelot/Headers/AddHeadersToRequest.cs
index d5a8c0629..d4a7bffab 100644
--- a/src/Ocelot/Headers/AddHeadersToRequest.cs
+++ b/src/Ocelot/Headers/AddHeadersToRequest.cs
@@ -64,7 +64,7 @@ public void SetHeadersOnDownstreamRequest(IEnumerable headers, HttpCo
if (value.IsError)
{
- _logger.LogWarning($"Unable to add header to response {header.Key}: {header.Value}");
+ _logger.LogWarning(() => $"Unable to add header to response {header.Key}: {header.Value}");
continue;
}
diff --git a/src/Ocelot/Headers/AddHeadersToResponse.cs b/src/Ocelot/Headers/AddHeadersToResponse.cs
index 5ab5c175c..8260d1051 100644
--- a/src/Ocelot/Headers/AddHeadersToResponse.cs
+++ b/src/Ocelot/Headers/AddHeadersToResponse.cs
@@ -26,7 +26,7 @@ public void Add(List addHeaders, DownstreamResponse response)
if (value.IsError)
{
- _logger.LogWarning($"Unable to add header to response {add.Key}: {add.Value}");
+ _logger.LogWarning(() => $"Unable to add header to response {add.Key}: {add.Value}");
continue;
}
diff --git a/src/Ocelot/Headers/Middleware/ClaimsToHeadersMiddleware.cs b/src/Ocelot/Headers/Middleware/ClaimsToHeadersMiddleware.cs
index 2feb14e0f..df8836409 100644
--- a/src/Ocelot/Headers/Middleware/ClaimsToHeadersMiddleware.cs
+++ b/src/Ocelot/Headers/Middleware/ClaimsToHeadersMiddleware.cs
@@ -24,7 +24,7 @@ public async Task Invoke(HttpContext httpContext)
if (downstreamRoute.ClaimsToHeaders.Any())
{
- Logger.LogInformation($"{downstreamRoute.DownstreamPathTemplate.Value} has instructions to convert claims to headers");
+ Logger.LogInformation(() => $"{downstreamRoute.DownstreamPathTemplate.Value} has instructions to convert claims to headers");
var downstreamRequest = httpContext.Items.DownstreamRequest();
diff --git a/src/Ocelot/Logging/AspDotNetLogger.cs b/src/Ocelot/Logging/AspDotNetLogger.cs
deleted file mode 100644
index c3aaf228e..000000000
--- a/src/Ocelot/Logging/AspDotNetLogger.cs
+++ /dev/null
@@ -1,93 +0,0 @@
-using Microsoft.Extensions.Logging;
-using Ocelot.Infrastructure.RequestData;
-
-namespace Ocelot.Logging
-{
- public class AspDotNetLogger : IOcelotLogger
- {
- private readonly ILogger _logger;
- private readonly IRequestScopedDataRepository _scopedDataRepository;
- private readonly Func _func;
-
- public AspDotNetLogger(ILogger logger, IRequestScopedDataRepository scopedDataRepository)
- {
- _logger = logger;
- _scopedDataRepository = scopedDataRepository;
- _func = (state, exception) => exception == null ? state : $"{state}, exception: {exception}";
- }
-
- public void LogTrace(string message)
- {
- var requestId = GetOcelotRequestId();
- var previousRequestId = GetOcelotPreviousRequestId();
-
- var state = $"requestId: {requestId}, previousRequestId: {previousRequestId}, message: {message}";
-
- _logger.Log(LogLevel.Trace, default, state, null, _func);
- }
-
- public void LogDebug(string message)
- {
- var requestId = GetOcelotRequestId();
- var previousRequestId = GetOcelotPreviousRequestId();
-
- var state = $"requestId: {requestId}, previousRequestId: {previousRequestId}, message: {message}";
-
- _logger.Log(LogLevel.Debug, default, state, null, _func);
- }
-
- public void LogInformation(string message)
- {
- var requestId = GetOcelotRequestId();
- var previousRequestId = GetOcelotPreviousRequestId();
-
- var state = $"requestId: {requestId}, previousRequestId: {previousRequestId}, message: {message}";
-
- _logger.Log(LogLevel.Information, default, state, null, _func);
- }
-
- public void LogWarning(string message)
- {
- var requestId = GetOcelotRequestId();
- var previousRequestId = GetOcelotPreviousRequestId();
-
- var state = $"requestId: {requestId}, previousRequestId: {previousRequestId}, message: {message}";
-
- _logger.Log(LogLevel.Warning, default, state, null, _func);
- }
-
- public void LogError(string message, Exception exception)
- {
- var requestId = GetOcelotRequestId();
- var previousRequestId = GetOcelotPreviousRequestId();
-
- var state = $"requestId: {requestId}, previousRequestId: {previousRequestId}, message: {message}";
-
- _logger.Log(LogLevel.Error, default, state, exception, _func);
- }
-
- public void LogCritical(string message, Exception exception)
- {
- var requestId = GetOcelotRequestId();
- var previousRequestId = GetOcelotPreviousRequestId();
-
- var state = $"requestId: {requestId}, previousRequestId: {previousRequestId}, message: {message}";
-
- _logger.Log(LogLevel.Critical, default, state, exception, _func);
- }
-
- private string GetOcelotRequestId()
- {
- var requestId = _scopedDataRepository.Get("RequestId");
-
- return requestId == null || requestId.IsError ? "no request id" : requestId.Data;
- }
-
- private string GetOcelotPreviousRequestId()
- {
- var requestId = _scopedDataRepository.Get("PreviousRequestId");
-
- return requestId == null || requestId.IsError ? "no previous request id" : requestId.Data;
- }
- }
-}
diff --git a/src/Ocelot/Logging/IOcelotLogger.cs b/src/Ocelot/Logging/IOcelotLogger.cs
index 4be17052e..5b8a68fd4 100644
--- a/src/Ocelot/Logging/IOcelotLogger.cs
+++ b/src/Ocelot/Logging/IOcelotLogger.cs
@@ -1,20 +1,28 @@
-namespace Ocelot.Logging
+using Ocelot.Configuration;
+using Ocelot.Infrastructure.RequestData;
+
+namespace Ocelot.Logging;
+
+///
+/// Thin wrapper around the .NET Core logging framework, used to allow the object to be injected giving access to the Ocelot .
+///
+public interface IOcelotLogger
{
- ///
- /// Thin wrapper around the DotNet core logging framework, used to allow the scopedDataRepository to be injected giving access to the Ocelot RequestId.
- ///
- public interface IOcelotLogger
- {
- void LogTrace(string message);
+ void LogTrace(string message);
+ void LogTrace(Func messageFactory);
- void LogDebug(string message);
+ void LogDebug(string message);
+ void LogDebug(Func messageFactory);
- void LogInformation(string message);
+ void LogInformation(string message);
+ void LogInformation(Func messageFactory);
- void LogWarning(string message);
+ void LogWarning(string message);
+ void LogWarning(Func messageFactory);
- void LogError(string message, Exception exception);
+ void LogError(string message, Exception exception);
+ void LogError(Func messageFactory, Exception exception);
- void LogCritical(string message, Exception exception);
- }
+ void LogCritical(string message, Exception exception);
+ void LogCritical(Func messageFactory, Exception exception);
}
diff --git a/src/Ocelot/Logging/OcelotDiagnosticListener.cs b/src/Ocelot/Logging/OcelotDiagnosticListener.cs
index 156ee1cc9..3cdb64aad 100644
--- a/src/Ocelot/Logging/OcelotDiagnosticListener.cs
+++ b/src/Ocelot/Logging/OcelotDiagnosticListener.cs
@@ -18,20 +18,20 @@ public OcelotDiagnosticListener(IOcelotLoggerFactory factory, IServiceProvider s
[DiagnosticName("Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareStarting")]
public virtual void OnMiddlewareStarting(HttpContext httpContext, string name)
{
- _logger.LogTrace($"MiddlewareStarting: {name}; {httpContext.Request.Path}");
+ _logger.LogTrace(() => $"MiddlewareStarting: {name}; {httpContext.Request.Path}");
Event(httpContext, $"MiddlewareStarting: {name}; {httpContext.Request.Path}");
}
[DiagnosticName("Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareException")]
public virtual void OnMiddlewareException(Exception exception, string name)
{
- _logger.LogTrace($"MiddlewareException: {name}; {exception.Message};");
+ _logger.LogTrace(() => $"MiddlewareException: {name}; {exception.Message};");
}
[DiagnosticName("Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareFinished")]
public virtual void OnMiddlewareFinished(HttpContext httpContext, string name)
{
- _logger.LogTrace($"MiddlewareFinished: {name}; {httpContext.Response.StatusCode}");
+ _logger.LogTrace(() => $"MiddlewareFinished: {name}; {httpContext.Response.StatusCode}");
Event(httpContext, $"MiddlewareFinished: {name}; {httpContext.Response.StatusCode}");
}
diff --git a/src/Ocelot/Logging/OcelotLogger.cs b/src/Ocelot/Logging/OcelotLogger.cs
new file mode 100644
index 000000000..1528f3957
--- /dev/null
+++ b/src/Ocelot/Logging/OcelotLogger.cs
@@ -0,0 +1,95 @@
+using Microsoft.Extensions.Logging;
+using Ocelot.Infrastructure.RequestData;
+using Ocelot.RequestId.Middleware;
+
+namespace Ocelot.Logging;
+
+///
+/// Default implementation of the interface.
+///
+public class OcelotLogger : IOcelotLogger
+{
+ private readonly ILogger _logger;
+ private readonly IRequestScopedDataRepository _scopedDataRepository;
+ private readonly Func _func;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Please note:
+ /// the log event message is designed to use placeholders ({RequestId}, {PreviousRequestId}, and {Message}).
+ /// If you're using a logger like Serilog, it will automatically capture these as structured data properties, making it easier to query and analyze the logs later.
+ ///
+ ///
+ /// The main logger type, per default the Microsoft implementation.
+ /// Repository, saving and getting data to/from HttpContext.Items.
+ /// The ILogger object is injected in OcelotLoggerFactory, it can't be verified before.
+ public OcelotLogger(ILogger logger, IRequestScopedDataRepository scopedDataRepository)
+ {
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ _scopedDataRepository = scopedDataRepository;
+ _func = (state, exception) => exception == null ? state : $"{state}, {nameof(exception)}: {exception}";
+ }
+
+ public void LogTrace(string message) => WriteLog(LogLevel.Trace, message);
+ public void LogTrace(Func messageFactory) => WriteLog(LogLevel.Trace, messageFactory);
+
+ public void LogDebug(string message) => WriteLog(LogLevel.Debug, message);
+ public void LogDebug(Func messageFactory) => WriteLog(LogLevel.Debug, messageFactory);
+
+ public void LogInformation(string message) => WriteLog(LogLevel.Information, message);
+ public void LogInformation(Func messageFactory) => WriteLog(LogLevel.Information, messageFactory);
+
+ public void LogWarning(string message) => WriteLog(LogLevel.Warning, message);
+ public void LogWarning(Func messageFactory) => WriteLog(LogLevel.Warning, messageFactory);
+
+ public void LogError(string message, Exception exception) => WriteLog(LogLevel.Error, message, exception);
+ public void LogError(Func messageFactory, Exception exception) => WriteLog(LogLevel.Error, messageFactory, exception);
+
+ public void LogCritical(string message, Exception exception) => WriteLog(LogLevel.Critical, message, exception);
+ public void LogCritical(Func messageFactory, Exception exception) => WriteLog(LogLevel.Critical, messageFactory, exception);
+
+ private string GetOcelotRequestId()
+ {
+ var requestId = _scopedDataRepository.Get(RequestIdMiddleware.RequestIdName);
+ return requestId?.IsError ?? true ? $"No {RequestIdMiddleware.RequestIdName}" : requestId.Data;
+ }
+
+ private string GetOcelotPreviousRequestId()
+ {
+ var requestId = _scopedDataRepository.Get(RequestIdMiddleware.PreviousRequestIdName);
+ return requestId?.IsError ?? true ? $"No {RequestIdMiddleware.PreviousRequestIdName}" : requestId.Data;
+ }
+
+ private void WriteLog(LogLevel logLevel, string message, Exception exception = null)
+ {
+ WriteLog(logLevel, null, message, exception);
+ }
+
+ private void WriteLog(LogLevel logLevel, Func messageFactory, Exception exception = null)
+ {
+ WriteLog(logLevel, messageFactory, null, exception);
+ }
+
+ private void WriteLog(LogLevel logLevel, Func messageFactory, string message, Exception exception = null)
+ {
+ if (!_logger.IsEnabled(logLevel))
+ {
+ return;
+ }
+
+ var requestId = GetOcelotRequestId();
+ var previousRequestId = GetOcelotPreviousRequestId();
+
+ if (messageFactory != null)
+ {
+ message = messageFactory.Invoke() ?? string.Empty;
+ }
+
+ _logger.Log(logLevel,
+ default,
+ $"{nameof(requestId)}: {requestId}, {nameof(previousRequestId)}: {previousRequestId}, {nameof(message)}: '{message}'",
+ exception,
+ _func);
+ }
+}
diff --git a/src/Ocelot/Logging/AspDotNetLoggerFactory.cs b/src/Ocelot/Logging/OcelotLoggerFactory.cs
similarity index 66%
rename from src/Ocelot/Logging/AspDotNetLoggerFactory.cs
rename to src/Ocelot/Logging/OcelotLoggerFactory.cs
index 8c53f6846..123caf2d2 100644
--- a/src/Ocelot/Logging/AspDotNetLoggerFactory.cs
+++ b/src/Ocelot/Logging/OcelotLoggerFactory.cs
@@ -3,12 +3,12 @@
namespace Ocelot.Logging
{
- public class AspDotNetLoggerFactory : IOcelotLoggerFactory
+ public class OcelotLoggerFactory : IOcelotLoggerFactory
{
private readonly ILoggerFactory _loggerFactory;
private readonly IRequestScopedDataRepository _scopedDataRepository;
- public AspDotNetLoggerFactory(ILoggerFactory loggerFactory, IRequestScopedDataRepository scopedDataRepository)
+ public OcelotLoggerFactory(ILoggerFactory loggerFactory, IRequestScopedDataRepository scopedDataRepository)
{
_loggerFactory = loggerFactory;
_scopedDataRepository = scopedDataRepository;
@@ -17,7 +17,7 @@ public AspDotNetLoggerFactory(ILoggerFactory loggerFactory, IRequestScopedDataRe
public IOcelotLogger CreateLogger()
{
var logger = _loggerFactory.CreateLogger();
- return new AspDotNetLogger(logger, _scopedDataRepository);
+ return new OcelotLogger(logger, _scopedDataRepository);
}
}
}
diff --git a/src/Ocelot/QueryStrings/Middleware/ClaimsToQueryStringMiddleware.cs b/src/Ocelot/QueryStrings/Middleware/ClaimsToQueryStringMiddleware.cs
index eb5c30fc8..0083fa453 100644
--- a/src/Ocelot/QueryStrings/Middleware/ClaimsToQueryStringMiddleware.cs
+++ b/src/Ocelot/QueryStrings/Middleware/ClaimsToQueryStringMiddleware.cs
@@ -24,7 +24,7 @@ public async Task Invoke(HttpContext httpContext)
if (downstreamRoute.ClaimsToQueries.Any())
{
- Logger.LogInformation($"{downstreamRoute.DownstreamPathTemplate.Value} has instructions to convert claims to queries");
+ Logger.LogInformation(() => $"{downstreamRoute.DownstreamPathTemplate.Value} has instructions to convert claims to queries");
var downstreamRequest = httpContext.Items.DownstreamRequest();
diff --git a/src/Ocelot/RateLimit/Middleware/ClientRateLimitMiddleware.cs b/src/Ocelot/RateLimit/Middleware/ClientRateLimitMiddleware.cs
index 6b62a14d2..47571046f 100644
--- a/src/Ocelot/RateLimit/Middleware/ClientRateLimitMiddleware.cs
+++ b/src/Ocelot/RateLimit/Middleware/ClientRateLimitMiddleware.cs
@@ -28,7 +28,7 @@ public async Task Invoke(HttpContext httpContext)
// check if rate limiting is enabled
if (!downstreamRoute.EnableEndpointEndpointRateLimiting)
{
- Logger.LogInformation($"EndpointRateLimiting is not enabled for {downstreamRoute.DownstreamPathTemplate.Value}");
+ Logger.LogInformation(() => $"EndpointRateLimiting is not enabled for {downstreamRoute.DownstreamPathTemplate.Value}");
await _next.Invoke(httpContext);
return;
}
@@ -39,7 +39,7 @@ public async Task Invoke(HttpContext httpContext)
// check white list
if (IsWhitelisted(identity, options))
{
- Logger.LogInformation($"{downstreamRoute.DownstreamPathTemplate.Value} is white listed from rate limiting");
+ Logger.LogInformation(() => $"{downstreamRoute.DownstreamPathTemplate.Value} is white listed from rate limiting");
await _next.Invoke(httpContext);
return;
}
@@ -110,7 +110,7 @@ public bool IsWhitelisted(ClientRequestIdentity requestIdentity, RateLimitOption
public virtual void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule, DownstreamRoute downstreamRoute)
{
Logger.LogInformation(
- $"Request {identity.HttpVerb}:{identity.Path} from ClientId {identity.ClientId} has been blocked, quota {rule.Limit}/{rule.Period} exceeded by {counter.TotalRequests}. Blocked by rule {downstreamRoute.UpstreamPathTemplate.OriginalValue}, TraceIdentifier {httpContext.TraceIdentifier}.");
+ () => $"Request {identity.HttpVerb}:{identity.Path} from ClientId {identity.ClientId} has been blocked, quota {rule.Limit}/{rule.Period} exceeded by {counter.TotalRequests}. Blocked by rule {downstreamRoute.UpstreamPathTemplate.OriginalValue}, TraceIdentifier {httpContext.TraceIdentifier}.");
}
public virtual DownstreamResponse ReturnQuotaExceededResponse(HttpContext httpContext, RateLimitOptions option, string retryAfter)
diff --git a/src/Ocelot/RequestId/Middleware/RequestIdMiddleware.cs b/src/Ocelot/RequestId/Middleware/RequestIdMiddleware.cs
index f2fc77ca1..5fef5a918 100644
--- a/src/Ocelot/RequestId/Middleware/RequestIdMiddleware.cs
+++ b/src/Ocelot/RequestId/Middleware/RequestIdMiddleware.cs
@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Http;
+using Ocelot.Configuration;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging;
using Ocelot.Middleware;
@@ -9,8 +10,12 @@ namespace Ocelot.RequestId.Middleware
{
public class RequestIdMiddleware : OcelotMiddleware
{
+ public const string RequestIdName = nameof(IInternalConfiguration.RequestId);
+ public const string PreviousRequestIdName = "Previous" + nameof(IInternalConfiguration.RequestId);
+
private readonly RequestDelegate _next;
private readonly IRequestScopedDataRepository _requestScopedDataRepository;
+
public RequestIdMiddleware(RequestDelegate next,
IOcelotLoggerFactory loggerFactory,
IRequestScopedDataRepository requestScopedDataRepository)
@@ -36,15 +41,15 @@ private void SetOcelotRequestId(HttpContext httpContext)
{
httpContext.TraceIdentifier = upstreamRequestIds.First();
- var previousRequestId = _requestScopedDataRepository.Get("RequestId");
+ var previousRequestId = _requestScopedDataRepository.Get(RequestIdName);
if (!previousRequestId.IsError && !string.IsNullOrEmpty(previousRequestId.Data) && previousRequestId.Data != httpContext.TraceIdentifier)
{
- _requestScopedDataRepository.Add("PreviousRequestId", previousRequestId.Data);
- _requestScopedDataRepository.Update("RequestId", httpContext.TraceIdentifier);
+ _requestScopedDataRepository.Add(PreviousRequestIdName, previousRequestId.Data);
+ _requestScopedDataRepository.Update(RequestIdName, httpContext.TraceIdentifier);
}
else
{
- _requestScopedDataRepository.Add("RequestId", httpContext.TraceIdentifier);
+ _requestScopedDataRepository.Add(RequestIdName, httpContext.TraceIdentifier);
}
}
diff --git a/src/Ocelot/Requester/DelegatingHandlerHandlerFactory.cs b/src/Ocelot/Requester/DelegatingHandlerHandlerFactory.cs
index 02b691c4c..8b7cc7955 100644
--- a/src/Ocelot/Requester/DelegatingHandlerHandlerFactory.cs
+++ b/src/Ocelot/Requester/DelegatingHandlerHandlerFactory.cs
@@ -71,7 +71,7 @@ public Response>> Get(DownstreamRoute downstreamRou
}
else
{
- _logger.LogWarning($"Route {downstreamRoute.UpstreamPathTemplate} specifies use QoS but no QosHandler found in DI container. Will use not use a QosHandler, please check your setup!");
+ _logger.LogWarning(() => $"Route {downstreamRoute.UpstreamPathTemplate} specifies use QoS but no QosHandler found in DI container. Will use not use a QosHandler, please check your setup!");
handlers.Add(() => new NoQosDelegatingHandler());
}
}
diff --git a/src/Ocelot/Requester/HttpClientBuilder.cs b/src/Ocelot/Requester/HttpClientBuilder.cs
index 0fd818e66..99b2bec3e 100644
--- a/src/Ocelot/Requester/HttpClientBuilder.cs
+++ b/src/Ocelot/Requester/HttpClientBuilder.cs
@@ -47,7 +47,7 @@ public IHttpClient Create(DownstreamRoute downstreamRoute)
HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
_logger
- .LogWarning($"You have ignored all SSL warnings by using DangerousAcceptAnyServerCertificateValidator for this DownstreamRoute, UpstreamPathTemplate: {downstreamRoute.UpstreamPathTemplate}, DownstreamPathTemplate: {downstreamRoute.DownstreamPathTemplate}");
+ .LogWarning(() => $"You have ignored all SSL warnings by using DangerousAcceptAnyServerCertificateValidator for this DownstreamRoute, UpstreamPathTemplate: {downstreamRoute.UpstreamPathTemplate}, DownstreamPathTemplate: {downstreamRoute.DownstreamPathTemplate}");
}
var timeout = downstreamRoute.QosOptions.TimeoutValue == 0
diff --git a/src/Ocelot/Requester/HttpExceptionToErrorMapper.cs b/src/Ocelot/Requester/HttpExceptionToErrorMapper.cs
index 80b7b15e8..62b03cfa9 100644
--- a/src/Ocelot/Requester/HttpExceptionToErrorMapper.cs
+++ b/src/Ocelot/Requester/HttpExceptionToErrorMapper.cs
@@ -16,9 +16,9 @@ public Error Map(Exception exception)
{
var type = exception.GetType();
- if (_mappers != null && _mappers.ContainsKey(type))
+ if (_mappers != null && _mappers.TryGetValue(type, out var mapper))
{
- return _mappers[type](exception);
+ return mapper(exception);
}
if (type == typeof(OperationCanceledException) || type.IsSubclassOf(typeof(OperationCanceledException)))
diff --git a/src/Ocelot/Requester/Middleware/HttpRequesterMiddleware.cs b/src/Ocelot/Requester/Middleware/HttpRequesterMiddleware.cs
index 5a56ab7a4..d996d2983 100644
--- a/src/Ocelot/Requester/Middleware/HttpRequesterMiddleware.cs
+++ b/src/Ocelot/Requester/Middleware/HttpRequesterMiddleware.cs
@@ -46,13 +46,13 @@ private void CreateLogBasedOnResponse(Response response)
{
if (response.Data?.StatusCode <= HttpStatusCode.BadRequest)
{
- Logger.LogInformation(
+ Logger.LogInformation(() =>
$"{(int)response.Data.StatusCode} ({response.Data.ReasonPhrase}) status code, request uri: {response.Data.RequestMessage?.RequestUri}");
}
else if (response.Data?.StatusCode >= HttpStatusCode.BadRequest)
{
Logger.LogWarning(
- $"{(int)response.Data.StatusCode} ({response.Data.ReasonPhrase}) status code, request uri: {response.Data.RequestMessage?.RequestUri}");
+ () => $"{(int)response.Data.StatusCode} ({response.Data.ReasonPhrase}) status code, request uri: {response.Data.RequestMessage?.RequestUri}");
}
}
}
diff --git a/src/Ocelot/Requester/QoS/QosFactory.cs b/src/Ocelot/Requester/QoS/QosFactory.cs
index 480116a74..be8569938 100644
--- a/src/Ocelot/Requester/QoS/QosFactory.cs
+++ b/src/Ocelot/Requester/QoS/QosFactory.cs
@@ -1,3 +1,4 @@
+using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Configuration;
using Ocelot.Logging;
@@ -8,12 +9,14 @@ namespace Ocelot.Requester.QoS
public class QoSFactory : IQoSFactory
{
private readonly IServiceProvider _serviceProvider;
- private readonly IOcelotLoggerFactory _ocelotLoggerFactory;
+ private readonly IOcelotLoggerFactory _ocelotLoggerFactory;
+ private readonly IHttpContextAccessor _contextAccessor;
- public QoSFactory(IServiceProvider serviceProvider, IOcelotLoggerFactory ocelotLoggerFactory)
+ public QoSFactory(IServiceProvider serviceProvider, IHttpContextAccessor contextAccessor, IOcelotLoggerFactory ocelotLoggerFactory)
{
_serviceProvider = serviceProvider;
- _ocelotLoggerFactory = ocelotLoggerFactory;
+ _ocelotLoggerFactory = ocelotLoggerFactory;
+ _contextAccessor = contextAccessor;
}
public Response Get(DownstreamRoute request)
@@ -22,7 +25,7 @@ public Response Get(DownstreamRoute request)
if (handler != null)
{
- return new OkResponse(handler(request, _ocelotLoggerFactory));
+ return new OkResponse(handler(request, _contextAccessor, _ocelotLoggerFactory));
}
return new ErrorResponse(new UnableToFindQoSProviderError($"could not find qosProvider for {request.DownstreamScheme}{request.DownstreamAddresses}{request.DownstreamPathTemplate}"));
diff --git a/src/Ocelot/Requester/QosDelegatingHandlerDelegate.cs b/src/Ocelot/Requester/QosDelegatingHandlerDelegate.cs
index 992601cb0..5af77f60b 100644
--- a/src/Ocelot/Requester/QosDelegatingHandlerDelegate.cs
+++ b/src/Ocelot/Requester/QosDelegatingHandlerDelegate.cs
@@ -1,7 +1,8 @@
-using Ocelot.Configuration;
-using Ocelot.Logging;
+using Microsoft.AspNetCore.Http;
+using Ocelot.Configuration;
+using Ocelot.Logging;
namespace Ocelot.Requester
{
- public delegate DelegatingHandler QosDelegatingHandlerDelegate(DownstreamRoute route, IOcelotLoggerFactory logger);
+ public delegate DelegatingHandler QosDelegatingHandlerDelegate(DownstreamRoute route, IHttpContextAccessor contextAccessor, IOcelotLoggerFactory loggerFactory);
}
diff --git a/src/Ocelot/Responder/Middleware/ResponderMiddleware.cs b/src/Ocelot/Responder/Middleware/ResponderMiddleware.cs
index 925663f71..76db720a1 100644
--- a/src/Ocelot/Responder/Middleware/ResponderMiddleware.cs
+++ b/src/Ocelot/Responder/Middleware/ResponderMiddleware.cs
@@ -37,13 +37,13 @@ public async Task Invoke(HttpContext httpContext)
// todo check errors is ok
if (errors.Count > 0)
{
- Logger.LogWarning($"{errors.ToErrorString()} errors found in {MiddlewareName}. Setting error response for request path:{httpContext.Request.Path}, request method: {httpContext.Request.Method}");
+ Logger.LogWarning(() => $"{errors.ToErrorString()} errors found in {MiddlewareName}. Setting error response for request path:{httpContext.Request.Path}, request method: {httpContext.Request.Method}");
SetErrorResponse(httpContext, errors);
}
else if (downstreamResponse == null)
{
- Logger.LogDebug($"Pipeline was terminated early in {MiddlewareName}");
+ Logger.LogDebug(() => $"Pipeline was terminated early in {MiddlewareName}");
}
else
{
diff --git a/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs b/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs
index 174c973ab..b47e4d921 100644
--- a/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs
+++ b/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs
@@ -1,51 +1,51 @@
-using Microsoft.Extensions.DependencyInjection;
-using Ocelot.Configuration;
-using Ocelot.Logging;
-using Ocelot.Responses;
-using Ocelot.ServiceDiscovery.Configuration;
-using Ocelot.ServiceDiscovery.Providers;
-using Ocelot.Values;
+using Microsoft.Extensions.DependencyInjection;
+using Ocelot.Configuration;
+using Ocelot.Logging;
+using Ocelot.Responses;
+using Ocelot.ServiceDiscovery.Configuration;
+using Ocelot.ServiceDiscovery.Providers;
+using Ocelot.Values;
namespace Ocelot.ServiceDiscovery
{
public class ServiceDiscoveryProviderFactory : IServiceDiscoveryProviderFactory
{
- private readonly IServiceProvider _provider;
- private readonly ServiceDiscoveryFinderDelegate _delegates;
+ private readonly IServiceProvider _provider;
+ private readonly ServiceDiscoveryFinderDelegate _delegates;
private readonly IOcelotLogger _logger;
public ServiceDiscoveryProviderFactory(IOcelotLoggerFactory factory, IServiceProvider provider)
{
_provider = provider;
- _delegates = provider.GetService();
+ _delegates = provider.GetService();
_logger = factory.CreateLogger();
}
public Response Get(ServiceProviderConfiguration serviceConfig, DownstreamRoute route)
{
if (route.UseServiceDiscovery)
- {
- var routeName = route.UpstreamPathTemplate?.Template ?? route.ServiceName ?? string.Empty;
- _logger.LogInformation($"The {nameof(DownstreamRoute.UseServiceDiscovery)} mode of the route '{routeName}' is enabled.");
+ {
+ var routeName = route.UpstreamPathTemplate?.Template ?? route.ServiceName ?? string.Empty;
+ _logger.LogInformation(() => $"The {nameof(DownstreamRoute.UseServiceDiscovery)} mode of the route '{routeName}' is enabled.");
return GetServiceDiscoveryProvider(serviceConfig, route);
}
- var services = route.DownstreamAddresses
- .Select(address => new Service(
- route.ServiceName,
- new ServiceHostAndPort(address.Host, address.Port, route.DownstreamScheme),
- string.Empty,
- string.Empty,
- Enumerable.Empty()))
- .ToList();
+ var services = route.DownstreamAddresses
+ .Select(address => new Service(
+ route.ServiceName,
+ new ServiceHostAndPort(address.Host, address.Port, route.DownstreamScheme),
+ string.Empty,
+ string.Empty,
+ Enumerable.Empty()))
+ .ToList();
return new OkResponse(new ConfigurationServiceProvider(services));
}
private Response GetServiceDiscoveryProvider(ServiceProviderConfiguration config, DownstreamRoute route)
- {
- _logger.LogInformation($"Getting service discovery provider of {nameof(config.Type)} '{config.Type}'...");
-
+ {
+ _logger.LogInformation(() => $"Getting service discovery provider of {nameof(config.Type)} '{config.Type}'...");
+
if (config.Type?.ToLower() == "servicefabric")
{
var sfConfig = new ServiceFabricConfiguration(config.Host, config.Port, route.ServiceName);
@@ -61,10 +61,10 @@ private Response GetServiceDiscoveryProvider(ServiceP
return new OkResponse(provider);
}
}
-
- var message = $"Unable to find service discovery provider for {nameof(config.Type)}: '{config.Type}'!";
- _logger.LogWarning(message);
-
+
+ var message = $"Unable to find service discovery provider for {nameof(config.Type)}: '{config.Type}'!";
+ _logger.LogWarning(() => $"Unable to find service discovery provider for {nameof(config.Type)}: '{config.Type}'!");
+
return new ErrorResponse(new UnableToFindServiceDiscoveryProviderError(message));
}
}
diff --git a/src/Ocelot/WebSockets/WebSocketsProxyMiddleware.cs b/src/Ocelot/WebSockets/WebSocketsProxyMiddleware.cs
index 86b7d963f..385efc831 100644
--- a/src/Ocelot/WebSockets/WebSocketsProxyMiddleware.cs
+++ b/src/Ocelot/WebSockets/WebSocketsProxyMiddleware.cs
@@ -110,7 +110,7 @@ private async Task Proxy(HttpContext context, DownstreamRequest request, Downstr
if (route.DangerousAcceptAnyServerCertificateValidator)
{
client.Options.RemoteCertificateValidationCallback = (request, certificate, chain, errors) => true;
- Logger.LogWarning(string.Format(IgnoredSslWarningFormat, route.UpstreamPathTemplate, route.DownstreamPathTemplate));
+ Logger.LogWarning(() => string.Format(IgnoredSslWarningFormat, route.UpstreamPathTemplate, route.DownstreamPathTemplate));
}
foreach (var protocol in context.WebSockets.WebSocketRequestedProtocols)
@@ -139,7 +139,7 @@ private async Task Proxy(HttpContext context, DownstreamRequest request, Downstr
var scheme = request.Scheme;
if (!scheme.StartsWith(Uri.UriSchemeWs))
{
- Logger.LogWarning(string.Format(InvalidSchemeWarningFormat, scheme, request.ToUri()));
+ Logger.LogWarning(() => string.Format(InvalidSchemeWarningFormat, scheme, request.ToUri()));
request.Scheme = scheme == Uri.UriSchemeHttp ? Uri.UriSchemeWs
: scheme == Uri.UriSchemeHttps ? Uri.UriSchemeWss : scheme;
}
diff --git a/test/Ocelot.AcceptanceTests/AggregateTests.cs b/test/Ocelot.AcceptanceTests/AggregateTests.cs
index 5810bf457..7cb4c03e3 100644
--- a/test/Ocelot.AcceptanceTests/AggregateTests.cs
+++ b/test/Ocelot.AcceptanceTests/AggregateTests.cs
@@ -21,7 +21,7 @@ public AggregateTests()
[Fact]
public void should_fix_issue_597()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
Routes = new List
@@ -132,9 +132,9 @@ public void should_fix_issue_597()
[Fact]
public void should_return_response_200_with_advanced_aggregate_configs()
{
- var port1 = RandomPortFinder.GetRandomPort();
- var port2 = RandomPortFinder.GetRandomPort();
- var port3 = RandomPortFinder.GetRandomPort();
+ var port1 = PortFinder.GetRandomPort();
+ var port2 = PortFinder.GetRandomPort();
+ var port3 = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
Routes = new List
@@ -229,8 +229,8 @@ public void should_return_response_200_with_advanced_aggregate_configs()
[Fact]
public void should_return_response_200_with_simple_url_user_defined_aggregate()
{
- var port1 = RandomPortFinder.GetRandomPort();
- var port2 = RandomPortFinder.GetRandomPort();
+ var port1 = PortFinder.GetRandomPort();
+ var port2 = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
Routes = new List
@@ -289,7 +289,7 @@ public void should_return_response_200_with_simple_url_user_defined_aggregate()
this.Given(x => x.GivenServiceOneIsRunning($"http://localhost:{port1}", "/", 200, "{Hello from Laura}"))
.Given(x => x.GivenServiceTwoIsRunning($"http://localhost:{port2}", "/", 200, "{Hello from Tom}"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
- .And(x => _steps.GivenOcelotIsRunningWithSpecficAggregatorsRegisteredInDi())
+ .And(x => _steps.GivenOcelotIsRunningWithSpecificAggregatorsRegisteredInDi())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe(expected))
@@ -300,8 +300,8 @@ public void should_return_response_200_with_simple_url_user_defined_aggregate()
[Fact]
public void should_return_response_200_with_simple_url()
{
- var port1 = RandomPortFinder.GetRandomPort();
- var port2 = RandomPortFinder.GetRandomPort();
+ var port1 = PortFinder.GetRandomPort();
+ var port2 = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
Routes = new List
@@ -370,8 +370,8 @@ public void should_return_response_200_with_simple_url()
[Fact]
public void should_return_response_200_with_simple_url_one_service_404()
{
- var port1 = RandomPortFinder.GetRandomPort();
- var port2 = RandomPortFinder.GetRandomPort();
+ var port1 = PortFinder.GetRandomPort();
+ var port2 = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
Routes = new List
@@ -440,8 +440,8 @@ public void should_return_response_200_with_simple_url_one_service_404()
[Fact]
public void should_return_response_200_with_simple_url_both_service_404()
{
- var port1 = RandomPortFinder.GetRandomPort();
- var port2 = RandomPortFinder.GetRandomPort();
+ var port1 = PortFinder.GetRandomPort();
+ var port2 = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
Routes = new List
@@ -510,8 +510,8 @@ public void should_return_response_200_with_simple_url_both_service_404()
[Fact]
public void should_be_thread_safe()
{
- var port1 = RandomPortFinder.GetRandomPort();
- var port2 = RandomPortFinder.GetRandomPort();
+ var port1 = PortFinder.GetRandomPort();
+ var port2 = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
Routes = new List
diff --git a/test/Ocelot.AcceptanceTests/AuthenticationTests.cs b/test/Ocelot.AcceptanceTests/AuthenticationTests.cs
index 8316d926c..35329847f 100644
--- a/test/Ocelot.AcceptanceTests/AuthenticationTests.cs
+++ b/test/Ocelot.AcceptanceTests/AuthenticationTests.cs
@@ -26,7 +26,7 @@ public AuthenticationTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
- var identityServerPort = RandomPortFinder.GetRandomPort();
+ var identityServerPort = PortFinder.GetRandomPort();
_identityServerRootUrl = $"http://localhost:{identityServerPort}";
_options = o =>
{
@@ -41,7 +41,7 @@ public AuthenticationTests()
[Fact]
public void should_return_401_using_identity_server_access_token()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -82,7 +82,7 @@ public void should_return_401_using_identity_server_access_token()
[Fact]
public void should_return_response_200_using_identity_server()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -125,7 +125,7 @@ public void should_return_response_200_using_identity_server()
[Fact]
public void should_return_response_401_using_identity_server_with_token_requested_for_other_api()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -167,7 +167,7 @@ public void should_return_response_401_using_identity_server_with_token_requeste
[Fact]
public void should_return_201_using_identity_server_access_token()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -210,7 +210,7 @@ public void should_return_201_using_identity_server_access_token()
[Fact]
public void should_return_201_using_identity_server_reference_token()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -364,7 +364,7 @@ private void GivenThereIsAnIdentityServerOn(string url, string apiName, string a
_identityServerBuilder.Start();
- _steps.VerifyIdentiryServerStarted(url);
+ Steps.VerifyIdentityServerStarted(url);
}
public void Dispose()
diff --git a/test/Ocelot.AcceptanceTests/AuthorizationTests.cs b/test/Ocelot.AcceptanceTests/AuthorizationTests.cs
index f44add4ae..26dcfb1a4 100644
--- a/test/Ocelot.AcceptanceTests/AuthorizationTests.cs
+++ b/test/Ocelot.AcceptanceTests/AuthorizationTests.cs
@@ -22,7 +22,7 @@ public AuthorizationTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
- var identityServerPort = RandomPortFinder.GetRandomPort();
+ var identityServerPort = PortFinder.GetRandomPort();
_identityServerRootUrl = $"http://localhost:{identityServerPort}";
_options = o =>
{
@@ -37,7 +37,7 @@ public AuthorizationTests()
[Fact]
public void should_return_response_200_authorizing_route()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -97,7 +97,7 @@ public void should_return_response_200_authorizing_route()
[Fact]
public void should_return_response_403_authorizing_route()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -155,7 +155,7 @@ public void should_return_response_403_authorizing_route()
[Fact]
public void should_return_response_200_using_identity_server_with_allowed_scope()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -198,7 +198,7 @@ public void should_return_response_200_using_identity_server_with_allowed_scope(
[Fact]
public void should_return_response_403_using_identity_server_with_scope_not_allowed()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -241,7 +241,7 @@ public void should_return_response_403_using_identity_server_with_scope_not_allo
[Fact]
public void should_fix_issue_240()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -393,7 +393,7 @@ private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTo
_identityServerBuilder.Start();
- _steps.VerifyIdentiryServerStarted(url);
+ Steps.VerifyIdentityServerStarted(url);
}
private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, List users)
@@ -464,7 +464,7 @@ private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTo
_identityServerBuilder.Start();
- _steps.VerifyIdentiryServerStarted(url);
+ Steps.VerifyIdentityServerStarted(url);
}
public void Dispose()
diff --git a/test/Ocelot.AcceptanceTests/ButterflyTracingTests.cs b/test/Ocelot.AcceptanceTests/ButterflyTracingTests.cs
index c3a4f19e9..a90560f02 100644
--- a/test/Ocelot.AcceptanceTests/ButterflyTracingTests.cs
+++ b/test/Ocelot.AcceptanceTests/ButterflyTracingTests.cs
@@ -27,8 +27,8 @@ public ButterflyTracingTests(ITestOutputHelper output)
[Fact]
public void should_forward_tracing_information_from_ocelot_and_downstream_services()
{
- var port1 = RandomPortFinder.GetRandomPort();
- var port2 = RandomPortFinder.GetRandomPort();
+ var port1 = PortFinder.GetRandomPort();
+ var port2 = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
Routes = new List
@@ -74,7 +74,7 @@ public void should_forward_tracing_information_from_ocelot_and_downstream_servic
},
};
- var butterflyPort = RandomPortFinder.GetRandomPort();
+ var butterflyPort = PortFinder.GetRandomPort();
var butterflyUrl = $"http://localhost:{butterflyPort}";
this.Given(x => GivenFakeButterfly(butterflyUrl))
@@ -100,7 +100,7 @@ public void should_forward_tracing_information_from_ocelot_and_downstream_servic
[Fact]
public void should_return_tracing_header()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
Routes = new List
@@ -132,7 +132,7 @@ public void should_return_tracing_header()
},
};
- var butterflyPort = RandomPortFinder.GetRandomPort();
+ var butterflyPort = PortFinder.GetRandomPort();
var butterflyUrl = $"http://localhost:{butterflyPort}";
this.Given(x => GivenFakeButterfly(butterflyUrl))
diff --git a/test/Ocelot.AcceptanceTests/CachingTests.cs b/test/Ocelot.AcceptanceTests/CachingTests.cs
index b2ef34d6c..fc1f91000 100644
--- a/test/Ocelot.AcceptanceTests/CachingTests.cs
+++ b/test/Ocelot.AcceptanceTests/CachingTests.cs
@@ -17,7 +17,7 @@ public CachingTests()
[Fact]
public void should_return_cached_response()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -62,7 +62,7 @@ public void should_return_cached_response()
[Fact]
public void should_return_cached_response_with_expires_header()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -108,7 +108,7 @@ public void should_return_cached_response_with_expires_header()
[Fact]
public void should_return_cached_response_when_using_jsonserialized_cache()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -152,7 +152,7 @@ public void should_return_cached_response_when_using_jsonserialized_cache()
[Fact]
public void should_not_return_cached_response_as_ttl_expires()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
diff --git a/test/Ocelot.AcceptanceTests/CancelRequestTests.cs b/test/Ocelot.AcceptanceTests/CancelRequestTests.cs
index 983d98fc0..984160591 100644
--- a/test/Ocelot.AcceptanceTests/CancelRequestTests.cs
+++ b/test/Ocelot.AcceptanceTests/CancelRequestTests.cs
@@ -33,7 +33,7 @@ public CancelRequestTests()
[Fact]
public void Should_abort_service_work_when_cancelling_the_request()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
diff --git a/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs b/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs
index 64759aed2..202fe2d27 100644
--- a/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs
+++ b/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs
@@ -17,7 +17,7 @@ public CaseSensitiveRoutingTests()
[Fact]
public void should_return_response_200_when_global_ignore_case_sensitivity_set()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -52,7 +52,7 @@ public void should_return_response_200_when_global_ignore_case_sensitivity_set()
[Fact]
public void should_return_response_200_when_route_ignore_case_sensitivity_set()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -88,7 +88,7 @@ public void should_return_response_200_when_route_ignore_case_sensitivity_set()
[Fact]
public void should_return_response_404_when_route_respect_case_sensitivity_set()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -124,7 +124,7 @@ public void should_return_response_404_when_route_respect_case_sensitivity_set()
[Fact]
public void should_return_response_200_when_route_respect_case_sensitivity_set()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -160,7 +160,7 @@ public void should_return_response_200_when_route_respect_case_sensitivity_set()
[Fact]
public void should_return_response_404_when_global_respect_case_sensitivity_set()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -196,7 +196,7 @@ public void should_return_response_404_when_global_respect_case_sensitivity_set(
[Fact]
public void should_return_response_200_when_global_respect_case_sensitivity_set()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
diff --git a/test/Ocelot.AcceptanceTests/ClaimsToDownstreamPathTests.cs b/test/Ocelot.AcceptanceTests/ClaimsToDownstreamPathTests.cs
index 445239c4b..b3697f0d6 100644
--- a/test/Ocelot.AcceptanceTests/ClaimsToDownstreamPathTests.cs
+++ b/test/Ocelot.AcceptanceTests/ClaimsToDownstreamPathTests.cs
@@ -20,7 +20,7 @@ public class ClaimsToDownstreamPathTests : IDisposable
public ClaimsToDownstreamPathTests()
{
- var identityServerPort = RandomPortFinder.GetRandomPort();
+ var identityServerPort = PortFinder.GetRandomPort();
_identityServerRootUrl = $"http://localhost:{identityServerPort}";
_steps = new Steps();
_options = o =>
@@ -43,7 +43,7 @@ public void should_return_200_and_change_downstream_path()
SubjectId = "registered|1231231",
};
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -196,7 +196,7 @@ private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTo
_identityServerBuilder.Start();
- _steps.VerifyIdentiryServerStarted(url);
+ Steps.VerifyIdentityServerStarted(url);
}
public void Dispose()
diff --git a/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs b/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs
index b8f2ece35..18cf8cdd0 100644
--- a/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs
+++ b/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs
@@ -24,7 +24,7 @@ public ClaimsToHeadersForwardingTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
- var identityServerPort = RandomPortFinder.GetRandomPort();
+ var identityServerPort = PortFinder.GetRandomPort();
_identityServerRootUrl = $"http://localhost:{identityServerPort}";
_options = o =>
{
@@ -51,7 +51,7 @@ public void should_return_response_200_and_foward_claim_as_header()
},
};
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -190,7 +190,7 @@ private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTo
_identityServerBuilder.Start();
- _steps.VerifyIdentiryServerStarted(url);
+ Steps.VerifyIdentityServerStarted(url);
}
public void Dispose()
diff --git a/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs b/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs
index 969faebb0..9a89fef62 100644
--- a/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs
+++ b/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs
@@ -22,7 +22,7 @@ public class ClaimsToQueryStringForwardingTests : IDisposable
public ClaimsToQueryStringForwardingTests()
{
_steps = new Steps();
- var identityServerPort = RandomPortFinder.GetRandomPort();
+ var identityServerPort = PortFinder.GetRandomPort();
_identityServerRootUrl = $"http://localhost:{identityServerPort}";
_options = o =>
{
@@ -49,7 +49,7 @@ public void should_return_response_200_and_foward_claim_as_query_string()
},
};
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -115,7 +115,7 @@ public void should_return_response_200_and_foward_claim_as_query_string_and_pres
},
};
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -277,7 +277,7 @@ private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTo
_identityServerBuilder.Start();
- _steps.VerifyIdentiryServerStarted(url);
+ Steps.VerifyIdentityServerStarted(url);
}
public void Dispose()
diff --git a/test/Ocelot.AcceptanceTests/ClientRateLimitTests.cs b/test/Ocelot.AcceptanceTests/ClientRateLimitTests.cs
index 3b04ca849..dad9af3dc 100644
--- a/test/Ocelot.AcceptanceTests/ClientRateLimitTests.cs
+++ b/test/Ocelot.AcceptanceTests/ClientRateLimitTests.cs
@@ -18,7 +18,7 @@ public ClientRateLimitTests()
[Fact]
public void should_call_withratelimiting()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -78,7 +78,7 @@ public void should_call_withratelimiting()
[Fact]
public void should_wait_for_period_timespan_to_elapse_before_making_next_request()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -145,7 +145,7 @@ public void should_wait_for_period_timespan_to_elapse_before_making_next_request
[Fact]
public void should_call_middleware_withWhitelistClient()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
diff --git a/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs b/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs
index ef35a8efc..c889f353e 100644
--- a/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs
+++ b/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs
@@ -27,8 +27,8 @@ public ConfigurationInConsulTests()
[Fact]
public void should_return_response_200_with_simple_url_when_using_jsonserialized_cache()
{
- var consulPort = RandomPortFinder.GetRandomPort();
- var servicePort = RandomPortFinder.GetRandomPort();
+ var consulPort = PortFinder.GetRandomPort();
+ var servicePort = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
diff --git a/test/Ocelot.AcceptanceTests/ConsulConfigurationInConsulTests.cs b/test/Ocelot.AcceptanceTests/ConsulConfigurationInConsulTests.cs
index 6d0c31a3a..978626c14 100644
--- a/test/Ocelot.AcceptanceTests/ConsulConfigurationInConsulTests.cs
+++ b/test/Ocelot.AcceptanceTests/ConsulConfigurationInConsulTests.cs
@@ -26,8 +26,8 @@ public ConsulConfigurationInConsulTests()
[Fact]
public void should_return_response_200_with_simple_url()
{
- var consulPort = RandomPortFinder.GetRandomPort();
- var servicePort = RandomPortFinder.GetRandomPort();
+ var consulPort = PortFinder.GetRandomPort();
+ var servicePort = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -75,8 +75,8 @@ public void should_return_response_200_with_simple_url()
[Fact]
public void should_load_configuration_out_of_consul()
{
- var consulPort = RandomPortFinder.GetRandomPort();
- var servicePort = RandomPortFinder.GetRandomPort();
+ var consulPort = PortFinder.GetRandomPort();
+ var servicePort = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -138,8 +138,8 @@ public void should_load_configuration_out_of_consul()
[Fact]
public void should_load_configuration_out_of_consul_if_it_is_changed()
{
- var consulPort = RandomPortFinder.GetRandomPort();
- var servicePort = RandomPortFinder.GetRandomPort();
+ var consulPort = PortFinder.GetRandomPort();
+ var servicePort = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -234,9 +234,9 @@ public void should_load_configuration_out_of_consul_if_it_is_changed()
[Fact]
public void should_handle_request_to_consul_for_downstream_service_and_make_request_no_re_routes_and_rate_limit()
{
- var consulPort = RandomPortFinder.GetRandomPort();
+ var consulPort = PortFinder.GetRandomPort();
const string serviceName = "web";
- var downstreamServicePort = RandomPortFinder.GetRandomPort();
+ var downstreamServicePort = PortFinder.GetRandomPort();
var downstreamServiceOneUrl = $"http://localhost:{downstreamServicePort}";
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
var serviceEntryOne = new ServiceEntry
diff --git a/test/Ocelot.AcceptanceTests/ConsulWebSocketTests.cs b/test/Ocelot.AcceptanceTests/ConsulWebSocketTests.cs
index 38e4e18c8..dceafc0d4 100644
--- a/test/Ocelot.AcceptanceTests/ConsulWebSocketTests.cs
+++ b/test/Ocelot.AcceptanceTests/ConsulWebSocketTests.cs
@@ -28,14 +28,14 @@ public ConsulWebSocketTests()
[Fact]
public void ShouldProxyWebsocketInputToDownstreamServiceAndUseServiceDiscoveryAndLoadBalancer()
{
- var downstreamPort = RandomPortFinder.GetRandomPort();
+ var downstreamPort = PortFinder.GetRandomPort();
var downstreamHost = "localhost";
- var secondDownstreamPort = RandomPortFinder.GetRandomPort();
+ var secondDownstreamPort = PortFinder.GetRandomPort();
var secondDownstreamHost = "localhost";
var serviceName = "websockets";
- var consulPort = RandomPortFinder.GetRandomPort();
+ var consulPort = PortFinder.GetRandomPort();
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
var serviceEntryOne = new ServiceEntry
{
diff --git a/test/Ocelot.AcceptanceTests/ContentTests.cs b/test/Ocelot.AcceptanceTests/ContentTests.cs
index a976ddb70..ba95dfd22 100644
--- a/test/Ocelot.AcceptanceTests/ContentTests.cs
+++ b/test/Ocelot.AcceptanceTests/ContentTests.cs
@@ -20,7 +20,7 @@ public ContentTests()
[Fact]
public void should_not_add_content_type_or_content_length_headers()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -58,7 +58,7 @@ public void should_not_add_content_type_or_content_length_headers()
[Fact]
public void should_add_content_type_and_content_length_headers()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -99,7 +99,7 @@ public void should_add_content_type_and_content_length_headers()
[Fact]
public void should_add_default_content_type_header()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
diff --git a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs
index 5f9bc2926..5fc91dd8d 100644
--- a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs
+++ b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs
@@ -30,7 +30,7 @@ public void should_call_pre_query_string_builder_middleware()
},
};
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var fileConfiguration = new FileConfiguration
{
@@ -75,7 +75,7 @@ public void should_call_authorization_middleware()
},
};
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var fileConfiguration = new FileConfiguration
{
@@ -120,7 +120,7 @@ public void should_call_authentication_middleware()
},
};
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var fileConfiguration = new FileConfiguration
{
@@ -165,7 +165,7 @@ public void should_call_pre_error_middleware()
},
};
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var fileConfiguration = new FileConfiguration
{
@@ -210,7 +210,7 @@ public void should_call_pre_authorization_middleware()
},
};
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var fileConfiguration = new FileConfiguration
{
@@ -255,7 +255,7 @@ public void should_call_pre_http_authentication_middleware()
},
};
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var fileConfiguration = new FileConfiguration
{
@@ -301,7 +301,7 @@ public void should_not_throw_when_pipeline_terminates_early()
}),
};
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var fileConfiguration = new FileConfiguration
{
@@ -350,7 +350,7 @@ public void should_fix_issue_237()
return Task.CompletedTask;
};
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var fileConfiguration = new FileConfiguration
{
@@ -376,7 +376,7 @@ public void should_fix_issue_237()
this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "/test"))
.And(x => _steps.GivenThereIsAConfiguration(fileConfiguration))
- .And(x => _steps.GivenOcelotIsRunningWithMiddleareBeforePipeline(callback))
+ .And(x => _steps.GivenOcelotIsRunningWithMiddlewareBeforePipeline(callback))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound))
.BDDfy();
diff --git a/test/Ocelot.AcceptanceTests/EurekaServiceDiscoveryTests.cs b/test/Ocelot.AcceptanceTests/EurekaServiceDiscoveryTests.cs
index 1be8c7798..1b7c1e99f 100644
--- a/test/Ocelot.AcceptanceTests/EurekaServiceDiscoveryTests.cs
+++ b/test/Ocelot.AcceptanceTests/EurekaServiceDiscoveryTests.cs
@@ -26,7 +26,7 @@ public void should_use_eureka_service_discovery_and_make_request(bool dotnetRunn
Environment.SetEnvironmentVariable("DOTNET_RUNNING_IN_CONTAINER", dotnetRunningInContainer.ToString());
var eurekaPort = 8761;
var serviceName = "product";
- var downstreamServicePort = RandomPortFinder.GetRandomPort();
+ var downstreamServicePort = PortFinder.GetRandomPort();
var downstreamServiceOneUrl = $"http://localhost:{downstreamServicePort}";
var fakeEurekaServiceDiscoveryUrl = $"http://localhost:{eurekaPort}";
diff --git a/test/Ocelot.AcceptanceTests/GzipTests.cs b/test/Ocelot.AcceptanceTests/GzipTests.cs
index ca10deedc..753ce70a9 100644
--- a/test/Ocelot.AcceptanceTests/GzipTests.cs
+++ b/test/Ocelot.AcceptanceTests/GzipTests.cs
@@ -18,7 +18,7 @@ public GzipTests()
[Fact]
public void should_return_response_200_with_simple_url()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
diff --git a/test/Ocelot.AcceptanceTests/HeaderTests.cs b/test/Ocelot.AcceptanceTests/HeaderTests.cs
index 1a7979a1d..6b1060383 100644
--- a/test/Ocelot.AcceptanceTests/HeaderTests.cs
+++ b/test/Ocelot.AcceptanceTests/HeaderTests.cs
@@ -18,7 +18,7 @@ public HeaderTests()
[Fact]
public void should_transform_upstream_header()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -59,7 +59,7 @@ public void should_transform_upstream_header()
[Fact]
public void should_transform_downstream_header()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -99,7 +99,7 @@ public void should_transform_downstream_header()
[Fact]
public void should_fix_issue_190()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -143,7 +143,7 @@ public void should_fix_issue_190()
[Fact]
public void should_fix_issue_205()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -187,7 +187,7 @@ public void should_fix_issue_205()
[Fact]
public void should_fix_issue_417()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -235,7 +235,7 @@ public void should_fix_issue_417()
[Fact]
public void request_should_reuse_cookies_with_cookie_container()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -278,7 +278,7 @@ public void request_should_reuse_cookies_with_cookie_container()
[Fact]
public void request_should_have_own_cookies_no_cookie_container()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -321,7 +321,7 @@ public void request_should_have_own_cookies_no_cookie_container()
[Fact]
public void issue_474_should_not_put_spaces_in_header()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -358,7 +358,7 @@ public void issue_474_should_not_put_spaces_in_header()
[Fact]
public void issue_474_should_put_spaces_in_header()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
diff --git a/test/Ocelot.AcceptanceTests/HttpClientCachingTests.cs b/test/Ocelot.AcceptanceTests/HttpClientCachingTests.cs
index 39a42ad71..eca147ac8 100644
--- a/test/Ocelot.AcceptanceTests/HttpClientCachingTests.cs
+++ b/test/Ocelot.AcceptanceTests/HttpClientCachingTests.cs
@@ -20,7 +20,7 @@ public HttpClientCachingTests()
[Fact]
public void should_cache_one_http_client_same_re_route()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -62,7 +62,7 @@ public void should_cache_one_http_client_same_re_route()
[Fact]
public void should_cache_two_http_client_different_re_route()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
diff --git a/test/Ocelot.AcceptanceTests/HttpDelegatingHandlersTests.cs b/test/Ocelot.AcceptanceTests/HttpDelegatingHandlersTests.cs
index 51ef4051c..83f79e720 100644
--- a/test/Ocelot.AcceptanceTests/HttpDelegatingHandlersTests.cs
+++ b/test/Ocelot.AcceptanceTests/HttpDelegatingHandlersTests.cs
@@ -18,7 +18,7 @@ public HttpDelegatingHandlersTests()
[Fact]
public void should_call_re_route_ordered_specific_handlers()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -49,7 +49,7 @@ public void should_call_re_route_ordered_specific_handlers()
this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
- .And(x => _steps.GivenOcelotIsRunningWithSpecficHandlersRegisteredInDi())
+ .And(x => _steps.GivenOcelotIsRunningWithSpecificHandlersRegisteredInDi())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
@@ -60,7 +60,7 @@ public void should_call_re_route_ordered_specific_handlers()
[Fact]
public void should_call_global_di_handlers()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -97,7 +97,7 @@ public void should_call_global_di_handlers()
[Fact]
public void should_call_global_di_handlers_multiple_times()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -150,7 +150,7 @@ public void should_call_global_di_handlers_multiple_times()
[Fact]
public void should_call_global_di_handlers_with_dependency()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
diff --git a/test/Ocelot.AcceptanceTests/HttpTests.cs b/test/Ocelot.AcceptanceTests/HttpTests.cs
index 9a80a4519..532bd6276 100644
--- a/test/Ocelot.AcceptanceTests/HttpTests.cs
+++ b/test/Ocelot.AcceptanceTests/HttpTests.cs
@@ -18,7 +18,7 @@ public HttpTests()
[Fact]
public void should_return_response_200_when_using_http_one()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -55,7 +55,7 @@ public void should_return_response_200_when_using_http_one()
[Fact]
public void should_return_response_200_when_using_http_one_point_one()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -92,7 +92,7 @@ public void should_return_response_200_when_using_http_one_point_one()
[Fact]
public void should_return_response_200_when_using_http_two_point_zero()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -134,7 +134,7 @@ public void should_return_response_200_when_using_http_two_point_zero()
[Fact]
public void should_return_response_502_when_using_http_one_to_talk_to_server_running_http_two()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -176,7 +176,7 @@ public void should_return_response_502_when_using_http_one_to_talk_to_server_run
[Fact]
public void should_return_response_200_when_using_http_two_to_talk_to_server_running_http_one_point_one()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
diff --git a/test/Ocelot.AcceptanceTests/LoadBalancerTests.cs b/test/Ocelot.AcceptanceTests/LoadBalancerTests.cs
index dfc4c3486..f882868d6 100644
--- a/test/Ocelot.AcceptanceTests/LoadBalancerTests.cs
+++ b/test/Ocelot.AcceptanceTests/LoadBalancerTests.cs
@@ -25,8 +25,8 @@ public LoadBalancerTests()
[Fact]
public void should_load_balance_request_with_least_connection()
{
- var portOne = RandomPortFinder.GetRandomPort();
- var portTwo = RandomPortFinder.GetRandomPort();
+ var portOne = PortFinder.GetRandomPort();
+ var portTwo = PortFinder.GetRandomPort();
var downstreamServiceOneUrl = $"http://localhost:{portOne}";
var downstreamServiceTwoUrl = $"http://localhost:{portTwo}";
@@ -73,8 +73,8 @@ public void should_load_balance_request_with_least_connection()
[Fact]
public void should_load_balance_request_with_round_robin()
{
- var downstreamPortOne = RandomPortFinder.GetRandomPort();
- var downstreamPortTwo = RandomPortFinder.GetRandomPort();
+ var downstreamPortOne = PortFinder.GetRandomPort();
+ var downstreamPortTwo = PortFinder.GetRandomPort();
var downstreamServiceOneUrl = $"http://localhost:{downstreamPortOne}";
var downstreamServiceTwoUrl = $"http://localhost:{downstreamPortTwo}";
@@ -120,8 +120,8 @@ public void should_load_balance_request_with_round_robin()
[Fact]
public void should_load_balance_request_with_custom_load_balancer()
{
- var downstreamPortOne = RandomPortFinder.GetRandomPort();
- var downstreamPortTwo = RandomPortFinder.GetRandomPort();
+ var downstreamPortOne = PortFinder.GetRandomPort();
+ var downstreamPortTwo = PortFinder.GetRandomPort();
var downstreamServiceOneUrl = $"http://localhost:{downstreamPortOne}";
var downstreamServiceTwoUrl = $"http://localhost:{downstreamPortTwo}";
diff --git a/test/Ocelot.AcceptanceTests/LogLevelTests.cs b/test/Ocelot.AcceptanceTests/LogLevelTests.cs
new file mode 100644
index 000000000..f4fb67bdc
--- /dev/null
+++ b/test/Ocelot.AcceptanceTests/LogLevelTests.cs
@@ -0,0 +1,175 @@
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+using Ocelot.Configuration.File;
+using Serilog;
+using Serilog.Core;
+
+namespace Ocelot.AcceptanceTests;
+
+public class LogLevelTests : IDisposable
+{
+ private readonly Steps _steps;
+ private readonly ServiceHandler _serviceHandler;
+ private readonly string _logFileName;
+ private readonly string _appSettingsFileName;
+
+ private const string AppSettingsFormat =
+ "{{\"Logging\":{{\"LogLevel\":{{\"Default\":\"{0}\",\"System\":\"{0}\",\"Microsoft\":\"{0}\"}}}}}}";
+
+ public LogLevelTests()
+ {
+ _steps = new Steps();
+ _serviceHandler = new ServiceHandler();
+ _logFileName = $"ocelot_logs_{Guid.NewGuid()}.log";
+ _appSettingsFileName = $"appsettings_{Guid.NewGuid()}.json";
+ }
+
+ private void ThenMessagesAreLogged(string[] notAllowedMessageTypes, string[] allowedMessageTypes)
+ {
+ var logFilePath = GetLogFilePath();
+ var logFileContent = File.ReadAllText(logFilePath);
+ var logFileLines = logFileContent.Split(Environment.NewLine);
+
+ var logFileLinesWithLogLevel = logFileLines.Where(x => notAllowedMessageTypes.Any(x.Contains)).ToList();
+ logFileLinesWithLogLevel.Count.ShouldBe(0);
+
+ var logFileLinesWithAllowedLogLevel = logFileLines.Where(x => allowedMessageTypes.Any(x.Contains)).ToList();
+ logFileLinesWithAllowedLogLevel.Count.ShouldBe(2 * allowedMessageTypes.Length);
+ }
+
+ private void TestFactory(string[] notAllowedMessageTypes, string[] allowedMessageTypes, LogLevel level)
+ {
+ var port = PortFinder.GetRandomPort();
+ var configuration = new FileConfiguration
+ {
+ Routes = new List
+ {
+ new()
+ {
+ DownstreamPathTemplate = "/",
+ DownstreamHostAndPorts = new List
+ {
+ new()
+ {
+ Host = "localhost",
+ Port = port,
+ },
+ },
+ DownstreamScheme = "http",
+ UpstreamPathTemplate = "/",
+ UpstreamHttpMethod = new List { "Get" },
+ RequestIdKey = _steps.RequestIdKey,
+ },
+ },
+ };
+
+ var logger = GetLogger(level);
+ this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}"))
+ .And(x => _steps.GivenThereIsAConfiguration(configuration))
+ .And(x => _steps.GivenOcelotIsRunningWithMinimumLogLevel(logger, _appSettingsFileName))
+ .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
+ .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
+ .Then(x => _steps.Dispose())
+ .Then(x => logger.Dispose())
+ .Then(x => ThenMessagesAreLogged(notAllowedMessageTypes, allowedMessageTypes))
+ .BDDfy();
+ }
+
+ [Fact]
+ public void if_minimum_log_level_is_critical_then_only_critical_messages_are_logged() => TestFactory(new[] { "TRACE", "INFORMATION", "WARNING", "ERROR" }, new[] { "CRITICAL" }, LogLevel.Critical);
+
+ [Fact]
+ public void if_minimum_log_level_is_error_then_critical_and_error_are_logged() => TestFactory(new[] { "TRACE", "INFORMATION", "WARNING", "DEBUG" }, new[] { "CRITICAL", "ERROR" }, LogLevel.Error);
+
+ [Fact]
+ public void if_minimum_log_level_is_warning_then_critical_error_and_warning_are_logged() => TestFactory(new[] { "TRACE", "INFORMATION", "DEBUG" }, new[] { "CRITICAL", "ERROR", "WARNING" }, LogLevel.Warning);
+
+ [Fact]
+ public void if_minimum_log_level_is_information_then_critical_error_warning_and_information_are_logged() => TestFactory(new[] { "TRACE", "DEBUG" }, new[] { "CRITICAL", "ERROR", "WARNING", "INFORMATION" }, LogLevel.Information);
+
+ [Fact]
+ public void if_minimum_log_level_is_debug_then_critical_error_warning_information_and_debug_are_logged() => TestFactory(new[] { "TRACE" }, new[] { "DEBUG", "CRITICAL", "ERROR", "WARNING", "INFORMATION" }, LogLevel.Debug);
+
+ [Fact]
+ public void if_minimum_log_level_is_trace_then_critical_error_warning_information_debug_and_trace_are_logged() => TestFactory(Array.Empty(), new[] { "TRACE", "DEBUG", "CRITICAL", "ERROR", "WARNING", "INFORMATION" }, LogLevel.Trace);
+
+ private Logger GetLogger(LogLevel logLevel)
+ {
+ var logFilePath = ResetLogFile();
+ UpdateAppSettings(logLevel);
+ var logger = logLevel switch
+ {
+ LogLevel.Information => new LoggerConfiguration().MinimumLevel.Information()
+ .WriteTo.File(logFilePath)
+ .CreateLogger(),
+ LogLevel.Warning => new LoggerConfiguration().MinimumLevel.Warning()
+ .WriteTo.File(logFilePath)
+ .CreateLogger(),
+ LogLevel.Error => new LoggerConfiguration().MinimumLevel.Error()
+ .WriteTo.File(logFilePath)
+ .CreateLogger(),
+ LogLevel.Critical => new LoggerConfiguration().MinimumLevel.Fatal()
+ .WriteTo.File(logFilePath)
+ .CreateLogger(),
+ LogLevel.Debug => new LoggerConfiguration().MinimumLevel.Debug()
+ .WriteTo.File(logFilePath)
+ .CreateLogger(),
+ LogLevel.Trace => new LoggerConfiguration().MinimumLevel.Verbose()
+ .WriteTo.File(logFilePath)
+ .CreateLogger(),
+ LogLevel.None => new LoggerConfiguration()
+ .WriteTo.File(logFilePath)
+ .CreateLogger(),
+ _ => throw new ArgumentOutOfRangeException(nameof(logLevel), logLevel, null),
+ };
+ return logger;
+ }
+
+ private void UpdateAppSettings(LogLevel logLevel)
+ {
+ var appSettingsFilePath = Path.Combine(AppContext.BaseDirectory, _appSettingsFileName);
+ if (File.Exists(appSettingsFilePath))
+ {
+ File.Delete(appSettingsFilePath);
+ }
+
+ var appSettings = string.Format(AppSettingsFormat, Enum.GetName(typeof(LogLevel), logLevel));
+ File.WriteAllText(appSettingsFilePath, appSettings);
+ }
+
+ private string ResetLogFile()
+ {
+ var logFilePath = GetLogFilePath();
+ if (File.Exists(logFilePath))
+ {
+ File.Delete(logFilePath);
+ }
+
+ return logFilePath;
+ }
+
+ private string GetLogFilePath()
+ {
+ var logFilePath = Path.Combine(AppContext.BaseDirectory, _logFileName);
+ return logFilePath;
+ }
+
+ private void GivenThereIsAServiceRunningOn(string baseUrl)
+ {
+ _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, async context =>
+ {
+ context.Response.StatusCode = 200;
+ await context.Response.WriteAsync(string.Empty);
+ });
+ }
+
+ public void Dispose()
+ {
+ _serviceHandler?.Dispose();
+ _steps.Dispose();
+ ResetLogFile();
+ GC.SuppressFinalize(this);
+ }
+}
diff --git a/test/Ocelot.AcceptanceTests/MethodTests.cs b/test/Ocelot.AcceptanceTests/MethodTests.cs
index 6ea2977e4..c71fdd473 100644
--- a/test/Ocelot.AcceptanceTests/MethodTests.cs
+++ b/test/Ocelot.AcceptanceTests/MethodTests.cs
@@ -17,7 +17,7 @@ public MethodTests()
[Fact]
public void should_return_response_200_when_get_converted_to_post()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -53,7 +53,7 @@ public void should_return_response_200_when_get_converted_to_post()
[Fact]
public void should_return_response_200_when_get_converted_to_post_with_content()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -93,7 +93,7 @@ public void should_return_response_200_when_get_converted_to_post_with_content()
[Fact]
public void should_return_response_200_when_get_converted_to_get_with_content()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
diff --git a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj
index 760b03bae..390879fc4 100644
--- a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj
+++ b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj
@@ -38,12 +38,14 @@
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -79,6 +81,7 @@
+
@@ -91,6 +94,7 @@
+
@@ -103,6 +107,7 @@
+
diff --git a/test/Ocelot.AcceptanceTests/OpenTracingTests.cs b/test/Ocelot.AcceptanceTests/OpenTracingTests.cs
index 74468ae21..1f467afba 100644
--- a/test/Ocelot.AcceptanceTests/OpenTracingTests.cs
+++ b/test/Ocelot.AcceptanceTests/OpenTracingTests.cs
@@ -30,8 +30,8 @@ public OpenTracingTests(ITestOutputHelper output)
[Fact]
public void should_forward_tracing_information_from_ocelot_and_downstream_services()
{
- var port1 = RandomPortFinder.GetRandomPort();
- var port2 = RandomPortFinder.GetRandomPort();
+ var port1 = PortFinder.GetRandomPort();
+ var port2 = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
Routes = new List
@@ -77,7 +77,7 @@ public void should_forward_tracing_information_from_ocelot_and_downstream_servic
},
};
- var tracingPort = RandomPortFinder.GetRandomPort();
+ var tracingPort = PortFinder.GetRandomPort();
var tracingUrl = $"http://localhost:{tracingPort}";
var fakeTracer = new FakeTracer();
@@ -100,7 +100,7 @@ public void should_forward_tracing_information_from_ocelot_and_downstream_servic
[Fact]
public void should_return_tracing_header()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
Routes = new List
@@ -132,7 +132,7 @@ public void should_return_tracing_header()
},
};
- var butterflyPort = RandomPortFinder.GetRandomPort();
+ var butterflyPort = PortFinder.GetRandomPort();
var butterflyUrl = $"http://localhost:{butterflyPort}";
diff --git a/test/Ocelot.AcceptanceTests/PollyQoSTests.cs b/test/Ocelot.AcceptanceTests/PollyQoSTests.cs
index 9d8f3ec39..f15838169 100644
--- a/test/Ocelot.AcceptanceTests/PollyQoSTests.cs
+++ b/test/Ocelot.AcceptanceTests/PollyQoSTests.cs
@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Http;
+using Ocelot.Configuration;
using Ocelot.Configuration.File;
namespace Ocelot.AcceptanceTests
@@ -8,221 +9,150 @@ public class PollyQoSTests : IDisposable
private readonly Steps _steps;
private readonly ServiceHandler _serviceHandler;
- public PollyQoSTests()
- {
- _serviceHandler = new ServiceHandler();
- _steps = new Steps();
- }
-
- [Fact]
- public void Should_not_timeout()
- {
- var port = RandomPortFinder.GetRandomPort();
-
- var configuration = new FileConfiguration
- {
- Routes = new List
- {
- new()
- {
- DownstreamPathTemplate = "/",
- DownstreamHostAndPorts = new List
- {
- new()
- {
- Host = "localhost",
- Port = port,
- },
- },
- DownstreamScheme = "http",
- UpstreamPathTemplate = "/",
- UpstreamHttpMethod = new List { "Post" },
- QoSOptions = new FileQoSOptions
- {
- TimeoutValue = 1000,
- ExceptionsAllowedBeforeBreaking = 10,
- },
- },
- },
- };
-
- this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, string.Empty, 10))
- .And(x => _steps.GivenThereIsAConfiguration(configuration))
- .And(x => _steps.GivenOcelotIsRunningWithPolly())
- .And(x => _steps.GivenThePostHasContent("postContent"))
- .When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .BDDfy();
- }
-
- [Fact]
- public void Should_timeout()
- {
- var port = RandomPortFinder.GetRandomPort();
-
- var configuration = new FileConfiguration
- {
- Routes = new List
- {
- new()
- {
- DownstreamPathTemplate = "/",
- DownstreamHostAndPorts = new List
- {
- new()
- {
- Host = "localhost",
- Port = port,
- },
- },
- DownstreamScheme = "http",
- UpstreamPathTemplate = "/",
- UpstreamHttpMethod = new List { "Post" },
- QoSOptions = new FileQoSOptions
- {
- TimeoutValue = 10,
- },
- },
- },
- };
-
- this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 201, string.Empty, 1000))
- .And(x => _steps.GivenThereIsAConfiguration(configuration))
- .And(x => _steps.GivenOcelotIsRunningWithPolly())
- .And(x => _steps.GivenThePostHasContent("postContent"))
- .When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
- .BDDfy();
- }
-
- [Fact]
- public void Should_open_circuit_breaker_then_close()
- {
- var port = RandomPortFinder.GetRandomPort();
-
- var configuration = new FileConfiguration
- {
- Routes = new List
- {
- new()
- {
- DownstreamPathTemplate = "/",
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new()
- {
- Host = "localhost",
- Port = port,
- },
- },
- UpstreamPathTemplate = "/",
- UpstreamHttpMethod = new List { "Get" },
- QoSOptions = new FileQoSOptions
- {
- ExceptionsAllowedBeforeBreaking = 1,
- TimeoutValue = 500,
- DurationOfBreak = 1000,
- },
- },
- },
- };
-
- this.Given(x => x.GivenThereIsAPossiblyBrokenServiceRunningOn($"http://localhost:{port}", "Hello from Laura"))
- .Given(x => _steps.GivenThereIsAConfiguration(configuration))
- .Given(x => _steps.GivenOcelotIsRunningWithPolly())
- .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
- .Given(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Given(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
- .Given(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Given(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
- .Given(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Given(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
- .Given(x => GivenIWaitMilliseconds(3000))
- .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
- .BDDfy();
- }
-
- [Fact]
- public void Open_circuit_should_not_effect_different_route()
- {
- var port1 = RandomPortFinder.GetRandomPort();
- var port2 = RandomPortFinder.GetRandomPort();
-
- var configuration = new FileConfiguration
- {
- Routes = new List
- {
- new()
- {
- DownstreamPathTemplate = "/",
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new()
- {
- Host = "localhost",
- Port = port1,
- },
- },
- UpstreamPathTemplate = "/",
- UpstreamHttpMethod = new List { "Get" },
- QoSOptions = new FileQoSOptions
- {
- ExceptionsAllowedBeforeBreaking = 1,
- TimeoutValue = 500,
- DurationOfBreak = 1000,
- },
- },
- new()
- {
- DownstreamPathTemplate = "/",
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new()
- {
- Host = "localhost",
- Port = port2,
- },
- },
- UpstreamPathTemplate = "/working",
- UpstreamHttpMethod = new List { "Get" },
- },
- },
- };
-
- this.Given(x => x.GivenThereIsAPossiblyBrokenServiceRunningOn($"http://localhost:{port1}", "Hello from Laura"))
- .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port2}/", 200, "Hello from Tom", 0))
- .And(x => _steps.GivenThereIsAConfiguration(configuration))
- .And(x => _steps.GivenOcelotIsRunningWithPolly())
- .And(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
- .And(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
- .And(x => _steps.WhenIGetUrlOnTheApiGateway("/working"))
- .And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Tom"))
- .And(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
- .And(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
- .And(x => GivenIWaitMilliseconds(3000))
- .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
- .BDDfy();
- }
-
- private static void GivenIWaitMilliseconds(int ms)
- {
- Thread.Sleep(ms);
- }
+ public PollyQoSTests()
+ {
+ _serviceHandler = new ServiceHandler();
+ _steps = new Steps();
+ }
+
+ private static FileConfiguration FileConfigurationFactory(int port, QoSOptions options,
+ string httpMethod = nameof(HttpMethods.Get)) => new()
+ {
+ Routes = new List
+ {
+ new()
+ {
+ DownstreamPathTemplate = "/",
+ DownstreamScheme = Uri.UriSchemeHttp,
+ DownstreamHostAndPorts = new()
+ {
+ new("localhost", port),
+ },
+ UpstreamPathTemplate = "/",
+ UpstreamHttpMethod = new() {httpMethod},
+ QoSOptions = new FileQoSOptions(options),
+ },
+ },
+ };
+
+ [Fact]
+ public void Should_not_timeout()
+ {
+ var port = PortFinder.GetRandomPort();
+ var configuration = FileConfigurationFactory(port, new QoSOptions(10, 0, 1000, null), HttpMethods.Post);
+
+ this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, string.Empty, 10))
+ .And(x => _steps.GivenThereIsAConfiguration(configuration))
+ .And(x => _steps.GivenOcelotIsRunningWithPolly())
+ .And(x => _steps.GivenThePostHasContent("postContent"))
+ .When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
+ .BDDfy();
+ }
+
+ [Fact]
+ public void Should_timeout()
+ {
+ var port = PortFinder.GetRandomPort();
+ var configuration = FileConfigurationFactory(port, new QoSOptions(0, 0, 10, null), HttpMethods.Post);
+
+ this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 201, string.Empty, 1000))
+ .And(x => _steps.GivenThereIsAConfiguration(configuration))
+ .And(x => _steps.GivenOcelotIsRunningWithPolly())
+ .And(x => _steps.GivenThePostHasContent("postContent"))
+ .When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
+ .BDDfy();
+ }
+
+ [Fact]
+ public void Should_open_circuit_breaker_after_two_exceptions()
+ {
+ var port = PortFinder.GetRandomPort();
+ var configuration = FileConfigurationFactory(port, new QoSOptions(2, 5000, 100000, null));
+
+ this.Given(x => x.GivenThereIsABrokenServiceRunningOn($"http://localhost:{port}"))
+ .And(x => _steps.GivenThereIsAConfiguration(configuration))
+ .And(x => _steps.GivenOcelotIsRunningWithPolly())
+ .And(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .And(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .And(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
+ .BDDfy();
+ }
+
+ [Fact]
+ public void Should_open_circuit_breaker_then_close()
+ {
+ var port = PortFinder.GetRandomPort();
+ var configuration = FileConfigurationFactory(port, new QoSOptions(1, 500, 1000, null));
+
+ this.Given(x => x.GivenThereIsAPossiblyBrokenServiceRunningOn($"http://localhost:{port}", "Hello from Laura"))
+ .Given(x => _steps.GivenThereIsAConfiguration(configuration))
+ .Given(x => _steps.GivenOcelotIsRunningWithPolly())
+ .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
+ .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
+ .Given(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Given(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
+ .Given(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Given(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
+ .Given(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Given(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
+ .Given(x => GivenIWaitMilliseconds(3000))
+ .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
+ .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
+ .BDDfy();
+ }
+
+ [Fact]
+ public void Open_circuit_should_not_effect_different_route()
+ {
+ var port1 = PortFinder.GetRandomPort();
+ var port2 = PortFinder.GetRandomPort();
+ var qos1 = new QoSOptions(1, 1000, 500, null);
+
+ var configuration = FileConfigurationFactory(port1, qos1);
+ var route2 = configuration.Routes[0].Clone() as FileRoute;
+ route2.DownstreamHostAndPorts[0].Port = port2;
+ route2.UpstreamPathTemplate = "/working";
+ route2.QoSOptions = new();
+ configuration.Routes.Add(route2);
+
+ this.Given(x => x.GivenThereIsAPossiblyBrokenServiceRunningOn($"http://localhost:{port1}", "Hello from Laura"))
+ .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port2}/", 200, "Hello from Tom", 0))
+ .And(x => _steps.GivenThereIsAConfiguration(configuration))
+ .And(x => _steps.GivenOcelotIsRunningWithPolly())
+ .And(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
+ .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
+ .And(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
+ .And(x => _steps.WhenIGetUrlOnTheApiGateway("/working"))
+ .And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
+ .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Tom"))
+ .And(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
+ .And(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
+ .And(x => GivenIWaitMilliseconds(3000))
+ .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
+ .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
+ .BDDfy();
+ }
+
+ private static void GivenIWaitMilliseconds(int ms) => Thread.Sleep(ms);
+
+ private void GivenThereIsABrokenServiceRunningOn(string url)
+ {
+ _serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
+ {
+ context.Response.StatusCode = 500;
+ await context.Response.WriteAsync("this is an exception");
+ });
+ }
private void GivenThereIsAPossiblyBrokenServiceRunningOn(string url, string responseBody)
{
@@ -240,21 +170,21 @@ private void GivenThereIsAPossiblyBrokenServiceRunningOn(string url, string resp
});
}
- private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody, int timeout)
- {
- _serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
- {
- Thread.Sleep(timeout);
- context.Response.StatusCode = statusCode;
- await context.Response.WriteAsync(responseBody);
- });
- }
-
- public void Dispose()
- {
- _serviceHandler?.Dispose();
+ private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody, int timeout)
+ {
+ _serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
+ {
+ Thread.Sleep(timeout);
+ context.Response.StatusCode = statusCode;
+ await context.Response.WriteAsync(responseBody);
+ });
+ }
+
+ public void Dispose()
+ {
+ _serviceHandler?.Dispose();
_steps.Dispose();
- GC.SuppressFinalize(this);
- }
- }
+ GC.SuppressFinalize(this);
+ }
+ }
}
diff --git a/test/Ocelot.AcceptanceTests/RandomPortFinder.cs b/test/Ocelot.AcceptanceTests/RandomPortFinder.cs
deleted file mode 100644
index 24fdce68c..000000000
--- a/test/Ocelot.AcceptanceTests/RandomPortFinder.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using System.Collections.Concurrent;
-using System.Net.Sockets;
-
-namespace Ocelot.AcceptanceTests
-{
- public static class RandomPortFinder
- {
- private const int EndPortRange = 45000;
- private static int _currentPort = 20000;
- private static readonly object LockObj = new();
- private static readonly ConcurrentBag UsedPorts = new();
-
- public static int GetRandomPort()
- {
- lock (LockObj)
- {
- if (_currentPort > EndPortRange)
- {
- throw new Exception("Cannot find available port to bind to.");
- }
-
- var port = UsePort(_currentPort);
- _currentPort += 1;
- return port;
- }
- }
-
- private static int UsePort(int randomPort)
- {
- UsedPorts.Add(randomPort);
-
- var ipe = new IPEndPoint(IPAddress.Loopback, randomPort);
-
- using var socket = new Socket(ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
- socket.Bind(ipe);
- socket.Close();
- return randomPort;
- }
- }
-}
diff --git a/test/Ocelot.AcceptanceTests/ReasonPhraseTests.cs b/test/Ocelot.AcceptanceTests/ReasonPhraseTests.cs
index 20f537777..7bbae6c3e 100644
--- a/test/Ocelot.AcceptanceTests/ReasonPhraseTests.cs
+++ b/test/Ocelot.AcceptanceTests/ReasonPhraseTests.cs
@@ -18,7 +18,7 @@ public ReasonPhraseTests()
[Fact]
public void should_return_reason_phrase()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
diff --git a/test/Ocelot.AcceptanceTests/RequestIdTests.cs b/test/Ocelot.AcceptanceTests/RequestIdTests.cs
index e1de4f313..d5e291807 100644
--- a/test/Ocelot.AcceptanceTests/RequestIdTests.cs
+++ b/test/Ocelot.AcceptanceTests/RequestIdTests.cs
@@ -16,7 +16,7 @@ public RequestIdTests()
[Fact]
public void should_use_default_request_id_and_forward()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -52,7 +52,7 @@ public void should_use_default_request_id_and_forward()
[Fact]
public void should_use_request_id_and_forward()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -89,7 +89,7 @@ public void should_use_request_id_and_forward()
[Fact]
public void should_use_global_request_id_and_forward()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -130,7 +130,7 @@ public void should_use_global_request_id_and_forward()
[Fact]
public void should_use_global_request_id_create_and_forward()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
diff --git a/test/Ocelot.AcceptanceTests/ResponseCodeTests.cs b/test/Ocelot.AcceptanceTests/ResponseCodeTests.cs
index 58964fe0f..c72c1fb62 100644
--- a/test/Ocelot.AcceptanceTests/ResponseCodeTests.cs
+++ b/test/Ocelot.AcceptanceTests/ResponseCodeTests.cs
@@ -16,7 +16,7 @@ public ResponseCodeTests()
[Fact]
public void ShouldReturnResponse304WhenServiceReturns304()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
diff --git a/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs b/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs
index 926a0e36f..454b41455 100644
--- a/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs
+++ b/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs
@@ -14,7 +14,7 @@ public ReturnsErrorTests()
}
[Fact]
- public void should_return_bad_gateway_error_if_downstream_service_doesnt_respond()
+ public void Should_return_bad_gateway_error_if_downstream_service_doesnt_respond()
{
var configuration = new FileConfiguration
{
@@ -46,9 +46,9 @@ public void should_return_bad_gateway_error_if_downstream_service_doesnt_respond
}
[Fact]
- public void should_return_internal_server_error_if_downstream_service_returns_internal_server_error()
+ public void Should_return_internal_server_error_if_downstream_service_returns_internal_server_error()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -81,9 +81,9 @@ public void should_return_internal_server_error_if_downstream_service_returns_in
}
[Fact]
- public void should_log_warning_if_downstream_service_returns_internal_server_error()
+ public void Should_log_warning_if_downstream_service_returns_internal_server_error()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -111,7 +111,7 @@ public void should_log_warning_if_downstream_service_returns_internal_server_err
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningWithLogger())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Then(x => _steps.ThenWarningShouldBeLogged())
+ .Then(x => _steps.ThenWarningShouldBeLogged(2))
.BDDfy();
}
@@ -123,7 +123,8 @@ private void GivenThereIsAServiceRunningOn(string url)
public void Dispose()
{
_serviceHandler?.Dispose();
- _steps.Dispose();
+ _steps.Dispose();
+ GC.SuppressFinalize(this);
}
}
}
diff --git a/test/Ocelot.AcceptanceTests/RoutingTests.cs b/test/Ocelot.AcceptanceTests/RoutingTests.cs
index 7b808ad0a..243d973d8 100644
--- a/test/Ocelot.AcceptanceTests/RoutingTests.cs
+++ b/test/Ocelot.AcceptanceTests/RoutingTests.cs
@@ -18,7 +18,7 @@ public RoutingTests()
[Fact]
public void should_not_match_forward_slash_in_pattern_before_next_forward_slash()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
Routes = new List
@@ -63,7 +63,7 @@ public void should_return_response_404_when_no_configuration_at_all()
[Fact]
public void should_return_response_200_with_forward_slash_and_placeholder_only()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -99,7 +99,7 @@ public void should_return_response_200_with_forward_slash_and_placeholder_only()
[Fact]
public void should_return_response_200_favouring_forward_slash_with_path_route()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -150,7 +150,7 @@ public void should_return_response_200_favouring_forward_slash_with_path_route()
[Fact]
public void should_return_response_200_favouring_forward_slash()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
Routes = new List
@@ -200,7 +200,7 @@ public void should_return_response_200_favouring_forward_slash()
[Fact]
public void should_return_response_200_favouring_forward_slash_route_because_it_is_first()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -251,7 +251,7 @@ public void should_return_response_200_favouring_forward_slash_route_because_it_
[Fact]
public void should_return_response_200_with_nothing_and_placeholder_only()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -287,7 +287,7 @@ public void should_return_response_200_with_nothing_and_placeholder_only()
[Fact]
public void should_return_response_200_with_simple_url()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -323,7 +323,7 @@ public void should_return_response_200_with_simple_url()
[Fact]
public void Bug()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -376,7 +376,7 @@ public void Bug()
[Fact]
public void should_return_response_200_when_path_missing_forward_slash_as_first_char()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -412,7 +412,7 @@ public void should_return_response_200_when_path_missing_forward_slash_as_first_
[Fact]
public void should_return_response_200_when_host_has_trailing_slash()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -448,7 +448,7 @@ public void should_return_response_200_when_host_has_trailing_slash()
[Fact]
public void should_return_ok_when_upstream_url_ends_with_forward_slash_but_template_does_not()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -484,7 +484,7 @@ public void should_return_ok_when_upstream_url_ends_with_forward_slash_but_templ
[Fact]
public void should_return_not_found_when_upstream_url_ends_with_forward_slash_but_template_does_not()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -519,7 +519,7 @@ public void should_return_not_found_when_upstream_url_ends_with_forward_slash_bu
[Fact]
public void should_return_not_found()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -554,7 +554,7 @@ public void should_return_not_found()
[Fact]
public void should_return_response_200_with_complex_url()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -590,7 +590,7 @@ public void should_return_response_200_with_complex_url()
[Fact]
public void should_return_response_200_with_complex_url_that_starts_with_placeholder()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -626,7 +626,7 @@ public void should_return_response_200_with_complex_url_that_starts_with_placeho
[Fact]
public void should_not_add_trailing_slash_to_downstream_url()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -661,7 +661,7 @@ public void should_not_add_trailing_slash_to_downstream_url()
[Fact]
public void should_return_response_201_with_simple_url()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -697,7 +697,7 @@ public void should_return_response_201_with_simple_url()
[Fact]
public void should_return_response_201_with_complex_query_string()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -733,7 +733,7 @@ public void should_return_response_201_with_complex_query_string()
[Fact]
public void should_return_response_200_with_placeholder_for_final_url_path()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -769,7 +769,7 @@ public void should_return_response_200_with_placeholder_for_final_url_path()
[Fact]
public void should_return_response_201_with_simple_url_and_multiple_upstream_http_method()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -805,7 +805,7 @@ public void should_return_response_201_with_simple_url_and_multiple_upstream_htt
[Fact]
public void should_return_response_200_with_simple_url_and_any_upstream_http_method()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -841,7 +841,7 @@ public void should_return_response_200_with_simple_url_and_any_upstream_http_met
[Fact]
public void should_return_404_when_calling_upstream_route_with_no_matching_downstream_re_route_github_issue_134()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -893,7 +893,7 @@ public void should_return_404_when_calling_upstream_route_with_no_matching_downs
[Fact]
public void should_not_set_trailing_slash_on_url_template()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -930,7 +930,7 @@ public void should_not_set_trailing_slash_on_url_template()
[Fact]
public void should_use_priority()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -982,7 +982,7 @@ public void should_use_priority()
[Fact]
public void should_match_multiple_paths_with_catch_all()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -1018,7 +1018,7 @@ public void should_match_multiple_paths_with_catch_all()
[Fact]
public void should_fix_issue_271()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
diff --git a/test/Ocelot.AcceptanceTests/RoutingWithQueryStringTests.cs b/test/Ocelot.AcceptanceTests/RoutingWithQueryStringTests.cs
index 9fdff5378..5c34167ac 100644
--- a/test/Ocelot.AcceptanceTests/RoutingWithQueryStringTests.cs
+++ b/test/Ocelot.AcceptanceTests/RoutingWithQueryStringTests.cs
@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
-
+
namespace Ocelot.AcceptanceTests
{
public class RoutingWithQueryStringTests : IDisposable
@@ -15,11 +15,11 @@ public RoutingWithQueryStringTests()
}
[Fact]
- public void should_return_response_200_with_query_string_template()
+ public void Should_return_response_200_with_query_string_template()
{
var subscriptionId = Guid.NewGuid().ToString();
var unitId = Guid.NewGuid().ToString();
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -43,7 +43,7 @@ public void should_return_response_200_with_query_string_template()
},
};
- this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/api/subscriptions/{subscriptionId}/updates", $"?unitId={unitId}", 200, "Hello from Laura"))
+ this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/api/subscriptions/{subscriptionId}/updates", $"?unitId={unitId}", "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway($"/api/units/{subscriptionId}/{unitId}/updates"))
@@ -51,13 +51,86 @@ public void should_return_response_200_with_query_string_template()
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy();
}
+
+ [Theory(DisplayName = "1182: " + nameof(Should_return_200_with_query_string_template_different_keys))]
+ [InlineData("")]
+ [InlineData("&x=xxx")]
+ public void Should_return_200_with_query_string_template_different_keys(string additionalParams)
+ {
+ var subscriptionId = Guid.NewGuid().ToString();
+ var unitId = Guid.NewGuid().ToString();
+ var port = PortFinder.GetRandomPort();
+
+ var configuration = new FileConfiguration
+ {
+ Routes = new List
+ {
+ new()
+ {
+ DownstreamPathTemplate = "/api/subscriptions/{subscriptionId}/updates?unitId={unit}",
+ DownstreamScheme = "http",
+ DownstreamHostAndPorts = new List
+ {
+ new()
+ {
+ Host = "localhost",
+ Port = port,
+ },
+ },
+ UpstreamPathTemplate = "/api/units/{subscriptionId}/updates?unit={unit}",
+ UpstreamHttpMethod = new List { "Get" },
+ },
+ },
+ };
+ this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/api/subscriptions/{subscriptionId}/updates", $"?unitId={unitId}{additionalParams}", "Hello from Laura"))
+ .And(x => _steps.GivenThereIsAConfiguration(configuration))
+ .And(x => _steps.GivenOcelotIsRunning())
+ .When(x => _steps.WhenIGetUrlOnTheApiGateway($"/api/units/{subscriptionId}/updates?unit={unitId}{additionalParams}"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
+ .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
+ .BDDfy();
+ }
+
+ [Theory(DisplayName = "1174: " + nameof(Should_return_200_and_forward_query_parameters_without_duplicates))]
+ [InlineData("projectNumber=45&startDate=2019-12-12&endDate=2019-12-12", "endDate=2019-12-12&projectNumber=45&startDate=2019-12-12")]
+ [InlineData("$filter=ProjectNumber eq 45 and DateOfSale ge 2020-03-01T00:00:00z and DateOfSale le 2020-03-15T00:00:00z", "$filter=ProjectNumber%20eq%2045%20and%20DateOfSale%20ge%202020-03-01T00:00:00z%20and%20DateOfSale%20le%202020-03-15T00:00:00z")]
+ public void Should_return_200_and_forward_query_parameters_without_duplicates(string everythingelse, string expectedOrdered)
+ {
+ var port = PortFinder.GetRandomPort();
+ var configuration = new FileConfiguration
+ {
+ Routes = new List
+ {
+ new()
+ {
+ DownstreamPathTemplate = "/api/contracts?{everythingelse}",
+ DownstreamScheme = "http",
+ DownstreamHostAndPorts = new List
+ {
+ new() { Host = "localhost", Port = port },
+ },
+ UpstreamPathTemplate = "/contracts?{everythingelse}",
+ UpstreamHttpMethod = new() { "Get" },
+ },
+ },
+ };
+
+ this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/api/contracts", $"?{expectedOrdered}", "Hello from @sunilk3"))
+ .And(x => _steps.GivenThereIsAConfiguration(configuration))
+ .And(x => _steps.GivenOcelotIsRunning())
+ .When(x => _steps.WhenIGetUrlOnTheApiGateway($"/contracts?{everythingelse}"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
+ .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from @sunilk3"))
+ .BDDfy();
+ }
+
[Fact]
- public void should_return_response_200_with_odata_query_string()
+ public void Should_return_response_200_with_odata_query_string()
{
var subscriptionId = Guid.NewGuid().ToString();
var unitId = Guid.NewGuid().ToString();
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -81,7 +154,7 @@ public void should_return_response_200_with_odata_query_string()
},
};
- this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/odata/customers", "?$filter=Name%20eq%20'Sam'", 200, "Hello from Laura"))
+ this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/odata/customers", "?$filter=Name%20eq%20'Sam'", "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/odata/customers?$filter=Name eq 'Sam' "))
@@ -91,11 +164,11 @@ public void should_return_response_200_with_odata_query_string()
}
[Fact]
- public void should_return_response_200_with_query_string_upstream_template()
+ public void Should_return_response_200_with_query_string_upstream_template()
{
var subscriptionId = Guid.NewGuid().ToString();
var unitId = Guid.NewGuid().ToString();
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -119,7 +192,7 @@ public void should_return_response_200_with_query_string_upstream_template()
},
};
- this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/api/units/{subscriptionId}/{unitId}/updates", string.Empty, 200, "Hello from Laura"))
+ this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/api/units/{subscriptionId}/{unitId}/updates", string.Empty, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway($"/api/subscriptions/{subscriptionId}/updates?unitId={unitId}"))
@@ -129,11 +202,11 @@ public void should_return_response_200_with_query_string_upstream_template()
}
[Fact]
- public void should_return_response_404_with_query_string_upstream_template_no_query_string()
+ public void Should_return_response_404_with_query_string_upstream_template_no_query_string()
{
var subscriptionId = Guid.NewGuid().ToString();
var unitId = Guid.NewGuid().ToString();
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -157,7 +230,7 @@ public void should_return_response_404_with_query_string_upstream_template_no_qu
},
};
- this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/api/units/{subscriptionId}/{unitId}/updates", string.Empty, 200, "Hello from Laura"))
+ this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/api/units/{subscriptionId}/{unitId}/updates", string.Empty, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway($"/api/subscriptions/{subscriptionId}/updates"))
@@ -166,11 +239,11 @@ public void should_return_response_404_with_query_string_upstream_template_no_qu
}
[Fact]
- public void should_return_response_404_with_query_string_upstream_template_different_query_string()
+ public void Should_return_response_404_with_query_string_upstream_template_different_query_string()
{
var subscriptionId = Guid.NewGuid().ToString();
var unitId = Guid.NewGuid().ToString();
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -194,7 +267,7 @@ public void should_return_response_404_with_query_string_upstream_template_diffe
},
};
- this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/api/units/{subscriptionId}/{unitId}/updates", string.Empty, 200, "Hello from Laura"))
+ this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/api/units/{subscriptionId}/{unitId}/updates", string.Empty, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway($"/api/subscriptions/{subscriptionId}/updates?test=1"))
@@ -203,11 +276,11 @@ public void should_return_response_404_with_query_string_upstream_template_diffe
}
[Fact]
- public void should_return_response_200_with_query_string_upstream_template_multiple_params()
+ public void Should_return_response_200_with_query_string_upstream_template_multiple_params()
{
var subscriptionId = Guid.NewGuid().ToString();
var unitId = Guid.NewGuid().ToString();
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -231,24 +304,26 @@ public void should_return_response_200_with_query_string_upstream_template_multi
},
};
- this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/api/units/{subscriptionId}/{unitId}/updates", "?productId=1", 200, "Hello from Laura"))
+ this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/api/units/{subscriptionId}/{unitId}/updates", "?productId=1", "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway($"/api/subscriptions/{subscriptionId}/updates?unitId={unitId}&productId=1"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy();
- }
-
- // to reproduce 1288: query string should contain the placeholder name and value
- [Fact]
- public void should_copy_query_string_to_downstream_path_issue_1288()
+ }
+
+ ///
+ /// To reproduce 1288: query string should contain the placeholder name and value.
+ ///
+ [Fact(DisplayName = "1288: " + nameof(Should_copy_query_string_to_downstream_path))]
+ public void Should_copy_query_string_to_downstream_path()
{
var idName = "id";
var idValue = "3";
var queryName = idName + "1";
var queryValue = "2" + idValue + "12";
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -260,11 +335,7 @@ public void should_copy_query_string_to_downstream_path_issue_1288()
DownstreamScheme = "http",
DownstreamHostAndPorts = new List
{
- new FileHostAndPort
- {
- Host = "localhost",
- Port = port,
- },
+ new() { Host = "localhost", Port = port },
},
UpstreamPathTemplate = $"/safe/{{{idName}}}",
UpstreamHttpMethod = new List { "Get" },
@@ -272,7 +343,7 @@ public void should_copy_query_string_to_downstream_path_issue_1288()
},
};
- this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/cpx/t1/{idValue}", $"?{queryName}={queryValue}", 200, "Hello from Laura"))
+ this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/cpx/t1/{idValue}", $"?{queryName}={queryValue}", "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway($"/safe/{idValue}?{queryName}={queryValue}"))
@@ -281,18 +352,18 @@ public void should_copy_query_string_to_downstream_path_issue_1288()
.BDDfy();
}
- private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, string queryString, int statusCode, string responseBody)
+ private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, string queryString, string responseBody)
{
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
{
if ((context.Request.PathBase.Value != basePath) || context.Request.QueryString.Value != queryString)
{
- context.Response.StatusCode = 500;
+ context.Response.StatusCode = StatusCodes.Status500InternalServerError;
await context.Response.WriteAsync("downstream path didnt match base path");
}
else
{
- context.Response.StatusCode = statusCode;
+ context.Response.StatusCode = StatusCodes.Status200OK;
await context.Response.WriteAsync(responseBody);
}
});
@@ -301,7 +372,8 @@ private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, stri
public void Dispose()
{
_serviceHandler?.Dispose();
- _steps.Dispose();
+ _steps.Dispose();
+ GC.SuppressFinalize(this);
}
}
}
diff --git a/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs b/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs
index c9f093bf0..79e0dd505 100644
--- a/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs
+++ b/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs
@@ -26,9 +26,9 @@ public ServiceDiscoveryTests()
[Fact]
public void should_use_consul_service_discovery_and_load_balance_request()
{
- var consulPort = RandomPortFinder.GetRandomPort();
- var servicePort1 = RandomPortFinder.GetRandomPort();
- var servicePort2 = RandomPortFinder.GetRandomPort();
+ var consulPort = PortFinder.GetRandomPort();
+ var servicePort1 = PortFinder.GetRandomPort();
+ var servicePort2 = PortFinder.GetRandomPort();
var serviceName = "product";
var downstreamServiceOneUrl = $"http://localhost:{servicePort1}";
var downstreamServiceTwoUrl = $"http://localhost:{servicePort2}";
@@ -96,8 +96,8 @@ public void should_use_consul_service_discovery_and_load_balance_request()
[Fact]
public void should_handle_request_to_consul_for_downstream_service_and_make_request()
{
- var consulPort = RandomPortFinder.GetRandomPort();
- var servicePort = RandomPortFinder.GetRandomPort();
+ var consulPort = PortFinder.GetRandomPort();
+ var servicePort = PortFinder.GetRandomPort();
const string serviceName = "web";
var downstreamServiceOneUrl = $"http://localhost:{servicePort}";
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
@@ -152,9 +152,9 @@ public void should_handle_request_to_consul_for_downstream_service_and_make_requ
[Fact]
public void should_handle_request_to_consul_for_downstream_service_and_make_request_no_re_routes()
{
- var consulPort = RandomPortFinder.GetRandomPort();
+ var consulPort = PortFinder.GetRandomPort();
const string serviceName = "web";
- var downstreamServicePort = RandomPortFinder.GetRandomPort();
+ var downstreamServicePort = PortFinder.GetRandomPort();
var downstreamServiceOneUrl = $"http://localhost:{downstreamServicePort}";
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
var serviceEntryOne = new ServiceEntry
@@ -203,10 +203,10 @@ public void should_handle_request_to_consul_for_downstream_service_and_make_requ
[Fact]
public void should_use_consul_service_discovery_and_load_balance_request_no_re_routes()
{
- var consulPort = RandomPortFinder.GetRandomPort();
+ var consulPort = PortFinder.GetRandomPort();
var serviceName = "product";
- var serviceOnePort = RandomPortFinder.GetRandomPort();
- var serviceTwoPort = RandomPortFinder.GetRandomPort();
+ var serviceOnePort = PortFinder.GetRandomPort();
+ var serviceTwoPort = PortFinder.GetRandomPort();
var downstreamServiceOneUrl = $"http://localhost:{serviceOnePort}";
var downstreamServiceTwoUrl = $"http://localhost:{serviceTwoPort}";
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
@@ -264,9 +264,9 @@ public void should_use_consul_service_discovery_and_load_balance_request_no_re_r
public void should_use_token_to_make_request_to_consul()
{
var token = "abctoken";
- var consulPort = RandomPortFinder.GetRandomPort();
+ var consulPort = PortFinder.GetRandomPort();
var serviceName = "web";
- var servicePort = RandomPortFinder.GetRandomPort();
+ var servicePort = PortFinder.GetRandomPort();
var downstreamServiceOneUrl = $"http://localhost:{servicePort}";
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
var serviceEntryOne = new ServiceEntry
@@ -322,10 +322,10 @@ public void should_use_token_to_make_request_to_consul()
[Fact]
public void should_send_request_to_service_after_it_becomes_available_in_consul()
{
- var consulPort = RandomPortFinder.GetRandomPort();
+ var consulPort = PortFinder.GetRandomPort();
var serviceName = "product";
- var servicePort1 = RandomPortFinder.GetRandomPort();
- var servicePort2 = RandomPortFinder.GetRandomPort();
+ var servicePort1 = PortFinder.GetRandomPort();
+ var servicePort2 = PortFinder.GetRandomPort();
var downstreamServiceOneUrl = $"http://localhost:{servicePort1}";
var downstreamServiceTwoUrl = $"http://localhost:{servicePort2}";
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
@@ -401,9 +401,9 @@ public void should_send_request_to_service_after_it_becomes_available_in_consul(
[Fact]
public void should_handle_request_to_poll_consul_for_downstream_service_and_make_request()
{
- var consulPort = RandomPortFinder.GetRandomPort();
+ var consulPort = PortFinder.GetRandomPort();
const string serviceName = "web";
- var downstreamServicePort = RandomPortFinder.GetRandomPort();
+ var downstreamServicePort = PortFinder.GetRandomPort();
var downstreamServiceOneUrl = $"http://localhost:{downstreamServicePort}";
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
var serviceEntryOne = new ServiceEntry
diff --git a/test/Ocelot.AcceptanceTests/ServiceFabricTests.cs b/test/Ocelot.AcceptanceTests/ServiceFabricTests.cs
index 69361d8d7..87d511a33 100644
--- a/test/Ocelot.AcceptanceTests/ServiceFabricTests.cs
+++ b/test/Ocelot.AcceptanceTests/ServiceFabricTests.cs
@@ -18,7 +18,7 @@ public ServiceFabricTests()
[Fact]
public void should_fix_issue_555()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -56,7 +56,7 @@ public void should_fix_issue_555()
[Fact]
public void should_support_service_fabric_naming_and_dns_service_stateless_and_guest()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -94,7 +94,7 @@ public void should_support_service_fabric_naming_and_dns_service_stateless_and_g
[Fact]
public void should_support_service_fabric_naming_and_dns_service_statefull_and_actors()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -132,7 +132,7 @@ public void should_support_service_fabric_naming_and_dns_service_statefull_and_a
[Fact]
public void should_support_placeholder_in_service_fabric_service_name()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
diff --git a/test/Ocelot.AcceptanceTests/SslTests.cs b/test/Ocelot.AcceptanceTests/SslTests.cs
index f37f1b430..649285f12 100644
--- a/test/Ocelot.AcceptanceTests/SslTests.cs
+++ b/test/Ocelot.AcceptanceTests/SslTests.cs
@@ -18,7 +18,7 @@ public SslTests()
[Fact]
public void should_dangerous_accept_any_server_certificate_validator()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
@@ -55,7 +55,7 @@ public void should_dangerous_accept_any_server_certificate_validator()
[Fact]
public void should_not_dangerous_accept_any_server_certificate_validator()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
diff --git a/test/Ocelot.AcceptanceTests/StartupTests.cs b/test/Ocelot.AcceptanceTests/StartupTests.cs
index afd10e3cc..04672354e 100644
--- a/test/Ocelot.AcceptanceTests/StartupTests.cs
+++ b/test/Ocelot.AcceptanceTests/StartupTests.cs
@@ -20,7 +20,7 @@ public StartupTests()
[Fact]
public void should_not_try_and_write_to_disk_on_startup_when_not_using_admin_api()
{
- var port = RandomPortFinder.GetRandomPort();
+ var port = PortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
diff --git a/test/Ocelot.AcceptanceTests/Steps.cs b/test/Ocelot.AcceptanceTests/Steps.cs
index 0686dd036..d87391b32 100644
--- a/test/Ocelot.AcceptanceTests/Steps.cs
+++ b/test/Ocelot.AcceptanceTests/Steps.cs
@@ -26,6 +26,8 @@
using Ocelot.ServiceDiscovery.Providers;
using Ocelot.Tracing.Butterfly;
using Ocelot.Tracing.OpenTracing;
+using Serilog;
+using Serilog.Core;
using System.IO.Compression;
using System.Net.Http.Headers;
using System.Text;
@@ -34,30 +36,43 @@
using CookieHeaderValue = Microsoft.Net.Http.Headers.CookieHeaderValue;
using MediaTypeHeaderValue = System.Net.Http.Headers.MediaTypeHeaderValue;
-namespace Ocelot.AcceptanceTests
+namespace Ocelot.AcceptanceTests;
+
+public class Steps : IDisposable
{
- public class Steps : IDisposable
- {
- private TestServer _ocelotServer;
- private HttpClient _ocelotClient;
- private HttpResponseMessage _response;
- private HttpContent _postContent;
- private BearerToken _token;
- public string RequestIdKey = "OcRequestId";
- private readonly Random _random;
- private readonly string _ocelotConfigFileName;
- private IWebHostBuilder _webHostBuilder;
- private WebHostBuilder _ocelotBuilder;
- private IWebHost _ocelotHost;
- private IOcelotConfigurationChangeTokenSource _changeToken;
-
- public Steps()
- {
- _random = new();
- _ocelotConfigFileName = $"{Guid.NewGuid():N}-ocelot.json";
- }
+ private TestServer _ocelotServer;
+ private HttpClient _ocelotClient;
+ private HttpResponseMessage _response;
+ private HttpContent _postContent;
+ private BearerToken _token;
+ public string RequestIdKey = "OcRequestId";
+ private readonly Random _random;
+ private readonly string _ocelotConfigFileName;
+ private IWebHostBuilder _webHostBuilder;
+ private WebHostBuilder _ocelotBuilder;
+ private IWebHost _ocelotHost;
+ private IOcelotConfigurationChangeTokenSource _changeToken;
+
+ public Steps()
+ {
+ _random = new Random();
+ _ocelotConfigFileName = $"{Guid.NewGuid():N}-ocelot.json";
+ }
+
+ public async Task ThenConfigShouldBe(FileConfiguration fileConfig)
+ {
+ var internalConfigCreator = _ocelotServer.Host.Services.GetService();
+ var internalConfigRepo = _ocelotServer.Host.Services.GetService();
+
+ var internalConfig = internalConfigRepo.Get();
+ var config = await internalConfigCreator.Create(fileConfig);
- public async Task ThenConfigShouldBe(FileConfiguration fileConfig)
+ internalConfig.Data.RequestId.ShouldBe(config.Data.RequestId);
+ }
+
+ public async Task ThenConfigShouldBeWithTimeout(FileConfiguration fileConfig, int timeoutMs)
+ {
+ var result = await Wait.WaitFor(timeoutMs).Until(async () =>
{
var internalConfigCreator = _ocelotServer.Host.Services.GetService();
var internalConfigRepo = _ocelotServer.Host.Services.GetService();
@@ -65,1242 +80,1184 @@ public async Task ThenConfigShouldBe(FileConfiguration fileConfig)
var internalConfig = internalConfigRepo.Get();
var config = await internalConfigCreator.Create(fileConfig);
- internalConfig.Data.RequestId.ShouldBe(config.Data.RequestId);
- }
-
- public async Task ThenConfigShouldBeWithTimeout(FileConfiguration fileConfig, int timeoutMs)
- {
- var result = await Wait.WaitFor(timeoutMs).Until(async () =>
- {
- var internalConfigCreator = _ocelotServer.Host.Services.GetService();
- var internalConfigRepo = _ocelotServer.Host.Services.GetService();
-
- var internalConfig = internalConfigRepo.Get();
- var config = await internalConfigCreator.Create(fileConfig);
+ return internalConfig.Data.RequestId == config.Data.RequestId;
+ });
- return internalConfig.Data.RequestId == config.Data.RequestId;
- });
-
- result.ShouldBe(true);
- }
+ result.ShouldBe(true);
+ }
- public async Task StartFakeOcelotWithWebSockets()
- {
- _ocelotBuilder = new WebHostBuilder();
- _ocelotBuilder.ConfigureServices(s =>
+ public async Task StartFakeOcelotWithWebSockets()
+ {
+ _ocelotBuilder = new WebHostBuilder();
+ _ocelotBuilder.ConfigureServices(s =>
+ {
+ s.AddSingleton(_ocelotBuilder);
+ s.AddOcelot();
+ });
+ _ocelotBuilder.UseKestrel()
+ .UseUrls("http://localhost:5000")
+ .UseContentRoot(Directory.GetCurrentDirectory())
+ .ConfigureAppConfiguration((hostingContext, config) =>
{
- s.AddSingleton(_ocelotBuilder);
- s.AddOcelot();
- });
- _ocelotBuilder.UseKestrel()
- .UseUrls("http://localhost:5000")
- .UseContentRoot(Directory.GetCurrentDirectory())
- .ConfigureAppConfiguration((hostingContext, config) =>
- {
- config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
- var env = hostingContext.HostingEnvironment;
- config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
- .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
- config.AddJsonFile(_ocelotConfigFileName, false, false);
- config.AddEnvironmentVariables();
- })
- .ConfigureLogging((hostingContext, logging) =>
- {
- logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
- logging.AddConsole();
- })
- .Configure(app =>
- {
- app.UseWebSockets();
- app.UseOcelot().Wait();
- })
- .UseIISIntegration();
- _ocelotHost = _ocelotBuilder.Build();
- await _ocelotHost.StartAsync();
- }
-
- public async Task StartFakeOcelotWithWebSocketsWithConsul()
- {
- _ocelotBuilder = new WebHostBuilder();
- _ocelotBuilder.ConfigureServices(s =>
+ config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
+ var env = hostingContext.HostingEnvironment;
+ config.AddJsonFile("appsettings.json", true, false)
+ .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, false);
+ config.AddJsonFile(_ocelotConfigFileName, false, false);
+ config.AddEnvironmentVariables();
+ })
+ .ConfigureLogging((hostingContext, logging) =>
{
- s.AddSingleton(_ocelotBuilder);
- s.AddOcelot().AddConsul();
- });
- _ocelotBuilder.UseKestrel()
- .UseUrls("http://localhost:5000")
- .UseContentRoot(Directory.GetCurrentDirectory())
- .ConfigureAppConfiguration((hostingContext, config) =>
- {
- config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
- var env = hostingContext.HostingEnvironment;
- config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
- .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
- config.AddJsonFile(_ocelotConfigFileName, false, false);
- config.AddEnvironmentVariables();
- })
- .ConfigureLogging((hostingContext, logging) =>
- {
- logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
- logging.AddConsole();
- })
- .Configure(app =>
- {
- app.UseWebSockets();
- app.UseOcelot().Wait();
- })
- .UseIISIntegration();
- _ocelotHost = _ocelotBuilder.Build();
- await _ocelotHost.StartAsync();
- }
-
- public void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
- {
- var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration, Formatting.Indented);
- File.WriteAllText(_ocelotConfigFileName, jsonConfiguration);
- }
-
- private void DeleteOcelotConfig()
- {
- if (!File.Exists(_ocelotConfigFileName))
+ logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
+ logging.AddConsole();
+ })
+ .Configure(app =>
{
- return;
- }
+ app.UseWebSockets();
+ app.UseOcelot().Wait();
+ })
+ .UseIISIntegration();
+ _ocelotHost = _ocelotBuilder.Build();
+ await _ocelotHost.StartAsync();
+ }
- try
+ public async Task StartFakeOcelotWithWebSocketsWithConsul()
+ {
+ _ocelotBuilder = new WebHostBuilder();
+ _ocelotBuilder.ConfigureServices(s =>
+ {
+ s.AddSingleton(_ocelotBuilder);
+ s.AddOcelot().AddConsul();
+ });
+ _ocelotBuilder.UseKestrel()
+ .UseUrls("http://localhost:5000")
+ .UseContentRoot(Directory.GetCurrentDirectory())
+ .ConfigureAppConfiguration((hostingContext, config) =>
{
- File.Delete(_ocelotConfigFileName);
- }
- catch (Exception e)
+ config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
+ var env = hostingContext.HostingEnvironment;
+ config.AddJsonFile("appsettings.json", true, false)
+ .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, false);
+ config.AddJsonFile(_ocelotConfigFileName, false, false);
+ config.AddEnvironmentVariables();
+ })
+ .ConfigureLogging((hostingContext, logging) =>
{
- Console.WriteLine(e);
- }
- }
+ logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
+ logging.AddConsole();
+ })
+ .Configure(app =>
+ {
+ app.UseWebSockets();
+ app.UseOcelot().Wait();
+ })
+ .UseIISIntegration();
+ _ocelotHost = _ocelotBuilder.Build();
+ await _ocelotHost.StartAsync();
+ }
+
+ public void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
+ {
+ var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration, Formatting.Indented);
+ File.WriteAllText(_ocelotConfigFileName, jsonConfiguration);
+ }
- public void ThenTheResponseBodyHeaderIs(string key, string value)
+ private void DeleteOcelotConfig()
+ {
+ if (!File.Exists(_ocelotConfigFileName))
{
- var header = _response.Content.Headers.GetValues(key);
- header.First().ShouldBe(value);
+ return;
}
- public void GivenOcelotIsRunningReloadingConfig(bool shouldReload)
+ try
{
- _webHostBuilder = new WebHostBuilder();
-
- _webHostBuilder
- .ConfigureAppConfiguration((hostingContext, config) =>
- {
- config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
- var env = hostingContext.HostingEnvironment;
- config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
- .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
- config.AddJsonFile(_ocelotConfigFileName, optional: false, reloadOnChange: shouldReload);
- config.AddEnvironmentVariables();
- })
- .ConfigureServices(s =>
- {
- s.AddOcelot();
- })
- .Configure(app =>
- {
- app.UseOcelot().Wait();
- });
-
- _ocelotServer = new TestServer(_webHostBuilder);
-
- _ocelotClient = _ocelotServer.CreateClient();
+ File.Delete(_ocelotConfigFileName);
}
-
- public void GivenIHaveAChangeToken()
+ catch (Exception e)
{
- _changeToken = _ocelotServer.Host.Services.GetRequiredService();
+ Console.WriteLine(e);
}
+ }
- ///
- /// This is annoying cos it should be in the constructor but we need to set up the file before calling startup so its a step.
- ///
- public void GivenOcelotIsRunning()
- {
- _webHostBuilder = new WebHostBuilder();
+ public void ThenTheResponseBodyHeaderIs(string key, string value)
+ {
+ var header = _response.Content.Headers.GetValues(key);
+ header.First().ShouldBe(value);
+ }
- _webHostBuilder
- .ConfigureAppConfiguration((hostingContext, config) =>
- {
- config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
- var env = hostingContext.HostingEnvironment;
- config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
- .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
- config.AddJsonFile(_ocelotConfigFileName, false, false);
- config.AddEnvironmentVariables();
- })
- .ConfigureServices(s =>
- {
- s.AddOcelot();
- })
- .Configure(app =>
- {
- app.UseOcelot().Wait();
- });
+ public void GivenOcelotIsRunningReloadingConfig(bool shouldReload)
+ {
+ _webHostBuilder = new WebHostBuilder();
- _ocelotServer = new TestServer(_webHostBuilder);
+ _webHostBuilder
+ .ConfigureAppConfiguration((hostingContext, config) =>
+ {
+ config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
+ var env = hostingContext.HostingEnvironment;
+ config.AddJsonFile("appsettings.json", true, false)
+ .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, false);
+ config.AddJsonFile(_ocelotConfigFileName, false, shouldReload);
+ config.AddEnvironmentVariables();
+ })
+ .ConfigureServices(s => { s.AddOcelot(); })
+ .Configure(app => { app.UseOcelot().Wait(); });
+
+ _ocelotServer = new TestServer(_webHostBuilder);
+
+ _ocelotClient = _ocelotServer.CreateClient();
+ }
- _ocelotClient = _ocelotServer.CreateClient();
- }
+ public void GivenIHaveAChangeToken()
+ {
+ _changeToken = _ocelotServer.Host.Services.GetRequiredService();
+ }
- ///
- /// This is annoying cos it should be in the constructor but we need to set up the file before calling startup so its a step.
- ///
- /// The type.
- /// The delegate object to load balancer factory.
- public void GivenOcelotIsRunningWithCustomLoadBalancer(Func loadBalancerFactoryFunc)
- where T : ILoadBalancer
- {
- _webHostBuilder = new WebHostBuilder();
+ ///
+ /// This is annoying cos it should be in the constructor but we need to set up the file before calling startup so its a step.
+ ///
+ public void GivenOcelotIsRunning()
+ {
+ _webHostBuilder = new WebHostBuilder();
- _webHostBuilder
- .ConfigureAppConfiguration((hostingContext, config) =>
- {
- config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
- var env = hostingContext.HostingEnvironment;
- config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
- .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
- config.AddJsonFile(_ocelotConfigFileName, false, false);
- config.AddEnvironmentVariables();
- })
- .ConfigureServices(s =>
- {
- s.AddOcelot()
- .AddCustomLoadBalancer(loadBalancerFactoryFunc);
- })
- .Configure(app =>
- {
- app.UseOcelot().Wait();
- });
+ _webHostBuilder
+ .ConfigureAppConfiguration((hostingContext, config) =>
+ {
+ config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
+ var env = hostingContext.HostingEnvironment;
+ config.AddJsonFile("appsettings.json", true, false)
+ .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, false);
+ config.AddJsonFile(_ocelotConfigFileName, false, false);
+ config.AddEnvironmentVariables();
+ })
+ .ConfigureServices(s => { s.AddOcelot(); })
+ .Configure(app => { app.UseOcelot().Wait(); });
+
+ _ocelotServer = new TestServer(_webHostBuilder);
+
+ _ocelotClient = _ocelotServer.CreateClient();
+ }
- _ocelotServer = new TestServer(_webHostBuilder);
+ ///
+ /// This is annoying cos it should be in the constructor but we need to set up the file before calling startup so its a step.
+ ///
+ /// The type.
+ /// The delegate object to load balancer factory.
+ public void GivenOcelotIsRunningWithCustomLoadBalancer(
+ Func loadBalancerFactoryFunc)
+ where T : ILoadBalancer
+ {
+ _webHostBuilder = new WebHostBuilder();
- _ocelotClient = _ocelotServer.CreateClient();
- }
+ _webHostBuilder
+ .ConfigureAppConfiguration((hostingContext, config) =>
+ {
+ config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
+ var env = hostingContext.HostingEnvironment;
+ config.AddJsonFile("appsettings.json", true, false)
+ .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, false);
+ config.AddJsonFile(_ocelotConfigFileName, false, false);
+ config.AddEnvironmentVariables();
+ })
+ .ConfigureServices(s =>
+ {
+ s.AddOcelot()
+ .AddCustomLoadBalancer(loadBalancerFactoryFunc);
+ })
+ .Configure(app => { app.UseOcelot().Wait(); });
- public void GivenOcelotIsRunningWithConsul()
- {
- _webHostBuilder = new WebHostBuilder();
+ _ocelotServer = new TestServer(_webHostBuilder);
- _webHostBuilder
- .ConfigureAppConfiguration((hostingContext, config) =>
- {
- config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
- var env = hostingContext.HostingEnvironment;
- config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
- .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
- config.AddJsonFile(_ocelotConfigFileName, false, false);
- config.AddEnvironmentVariables();
- })
- .ConfigureServices(s =>
- {
- s.AddOcelot().AddConsul();
- })
- .Configure(app =>
- {
- app.UseOcelot().Wait();
- });
+ _ocelotClient = _ocelotServer.CreateClient();
+ }
- _ocelotServer = new TestServer(_webHostBuilder);
+ public void GivenOcelotIsRunningWithConsul()
+ {
+ _webHostBuilder = new WebHostBuilder();
- _ocelotClient = _ocelotServer.CreateClient();
- }
+ _webHostBuilder
+ .ConfigureAppConfiguration((hostingContext, config) =>
+ {
+ config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
+ var env = hostingContext.HostingEnvironment;
+ config.AddJsonFile("appsettings.json", true, false)
+ .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, false);
+ config.AddJsonFile(_ocelotConfigFileName, false, false);
+ config.AddEnvironmentVariables();
+ })
+ .ConfigureServices(s => { s.AddOcelot().AddConsul(); })
+ .Configure(app => { app.UseOcelot().Wait(); });
+
+ _ocelotServer = new TestServer(_webHostBuilder);
+
+ _ocelotClient = _ocelotServer.CreateClient();
+ }
- public void ThenTheTraceHeaderIsSet(string key)
- {
- var header = _response.Headers.GetValues(key);
- header.First().ShouldNotBeNullOrEmpty();
- }
+ public void ThenTheTraceHeaderIsSet(string key)
+ {
+ var header = _response.Headers.GetValues(key);
+ header.First().ShouldNotBeNullOrEmpty();
+ }
- internal void GivenOcelotIsRunningUsingButterfly(string butterflyUrl)
- {
- _webHostBuilder = new WebHostBuilder();
+ internal void GivenOcelotIsRunningUsingButterfly(string butterflyUrl)
+ {
+ _webHostBuilder = new WebHostBuilder();
- _webHostBuilder
- .ConfigureAppConfiguration((hostingContext, config) =>
- {
- config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
- var env = hostingContext.HostingEnvironment;
- config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
- .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
- config.AddJsonFile(_ocelotConfigFileName, optional: true, reloadOnChange: false);
- config.AddEnvironmentVariables();
- })
- .ConfigureServices(s =>
- {
- s.AddOcelot()
+ _webHostBuilder
+ .ConfigureAppConfiguration((hostingContext, config) =>
+ {
+ config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
+ var env = hostingContext.HostingEnvironment;
+ config.AddJsonFile("appsettings.json", true, false)
+ .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, false);
+ config.AddJsonFile(_ocelotConfigFileName, true, false);
+ config.AddEnvironmentVariables();
+ })
+ .ConfigureServices(s =>
+ {
+ s.AddOcelot()
.AddButterfly(option =>
{
//this is the url that the butterfly collector server is running on...
option.CollectorUrl = butterflyUrl;
option.Service = "Ocelot";
});
- })
- .Configure(app =>
- {
- app.Use(async (context, next) =>
- {
- await next.Invoke();
- });
- app.UseOcelot().Wait();
- });
+ })
+ .Configure(app =>
+ {
+ app.Use(async (_, next) => { await next.Invoke(); });
+ app.UseOcelot().Wait();
+ });
- _ocelotServer = new TestServer(_webHostBuilder);
+ _ocelotServer = new TestServer(_webHostBuilder);
- _ocelotClient = _ocelotServer.CreateClient();
- }
+ _ocelotClient = _ocelotServer.CreateClient();
+ }
- public void GivenOcelotIsRunningUsingConsulToStoreConfigAndJsonSerializedCache()
- {
- _webHostBuilder = new WebHostBuilder();
+ public void GivenOcelotIsRunningUsingConsulToStoreConfigAndJsonSerializedCache()
+ {
+ _webHostBuilder = new WebHostBuilder();
- _webHostBuilder
- .ConfigureAppConfiguration((hostingContext, config) =>
- {
- config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
- var env = hostingContext.HostingEnvironment;
- config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
- .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
- config.AddJsonFile(_ocelotConfigFileName, optional: true, reloadOnChange: false);
- config.AddEnvironmentVariables();
- })
- .ConfigureServices(s =>
- {
- s.AddOcelot()
- .AddCacheManager((x) =>
- {
- x.WithMicrosoftLogging(log =>
- {
- //log.AddConsole(LogLevel.Debug);
- })
- .WithJsonSerializer()
- .WithHandle(typeof(InMemoryJsonHandle<>));
- })
- .AddConsul()
- .AddConfigStoredInConsul();
- })
- .Configure(app =>
- {
- app.UseOcelot().Wait();
- });
+ _webHostBuilder
+ .ConfigureAppConfiguration((hostingContext, config) =>
+ {
+ config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
+ var env = hostingContext.HostingEnvironment;
+ config.AddJsonFile("appsettings.json", true, false)
+ .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, false);
+ config.AddJsonFile(_ocelotConfigFileName, true, false);
+ config.AddEnvironmentVariables();
+ })
+ .ConfigureServices(s =>
+ {
+ s.AddOcelot()
+ .AddCacheManager((x) =>
+ {
+ x.WithMicrosoftLogging(_ =>
+ {
+ //log.AddConsole(LogLevel.Debug);
+ })
+ .WithJsonSerializer()
+ .WithHandle(typeof(InMemoryJsonHandle<>));
+ })
+ .AddConsul()
+ .AddConfigStoredInConsul();
+ })
+ .Configure(app => { app.UseOcelot().Wait(); });
+
+ _ocelotServer = new TestServer(_webHostBuilder);
+
+ _ocelotClient = _ocelotServer.CreateClient();
+ }
- _ocelotServer = new TestServer(_webHostBuilder);
+ public void GivenOcelotIsRunningUsingConsulToStoreConfig()
+ {
+ _webHostBuilder = new WebHostBuilder();
- _ocelotClient = _ocelotServer.CreateClient();
- }
+ _webHostBuilder
+ .ConfigureAppConfiguration((hostingContext, config) =>
+ {
+ config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
+ var env = hostingContext.HostingEnvironment;
+ config.AddJsonFile("appsettings.json", true, false)
+ .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, false);
+ config.AddJsonFile(_ocelotConfigFileName, true, false);
+ config.AddEnvironmentVariables();
+ })
+ .ConfigureServices(s => { s.AddOcelot().AddConsul().AddConfigStoredInConsul(); })
+ .Configure(app => { app.UseOcelot().Wait(); });
+
+ _ocelotServer = new TestServer(_webHostBuilder);
+
+ _ocelotClient = _ocelotServer.CreateClient();
+ Thread.Sleep(1000);
+ }
- public void GivenOcelotIsRunningUsingConsulToStoreConfig()
+ public void WhenIGetUrlOnTheApiGatewayWaitingForTheResponseToBeOk(string url)
+ {
+ var result = Wait.WaitFor(2000).Until(() =>
{
- _webHostBuilder = new WebHostBuilder();
-
- _webHostBuilder
- .ConfigureAppConfiguration((hostingContext, config) =>
- {
- config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
- var env = hostingContext.HostingEnvironment;
- config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
- .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
- config.AddJsonFile(_ocelotConfigFileName, optional: true, reloadOnChange: false);
- config.AddEnvironmentVariables();
- })
- .ConfigureServices(s =>
- {
- s.AddOcelot().AddConsul().AddConfigStoredInConsul();
- })
- .Configure(app =>
- {
- app.UseOcelot().Wait();
- });
+ try
+ {
+ _response = _ocelotClient.GetAsync(url).Result;
+ _response.EnsureSuccessStatusCode();
+ return true;
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ });
- _ocelotServer = new TestServer(_webHostBuilder);
+ result.ShouldBeTrue();
+ }
- _ocelotClient = _ocelotServer.CreateClient();
- Thread.Sleep(1000);
- }
+ public void GivenOcelotIsRunningUsingJsonSerializedCache()
+ {
+ _webHostBuilder = new WebHostBuilder();
- public void WhenIGetUrlOnTheApiGatewayWaitingForTheResponseToBeOk(string url)
- {
- var result = Wait.WaitFor(2000).Until(() =>
+ _webHostBuilder
+ .ConfigureAppConfiguration((hostingContext, config) =>
{
- try
- {
- _response = _ocelotClient.GetAsync(url).Result;
- _response.EnsureSuccessStatusCode();
- return true;
- }
- catch (Exception)
- {
- return false;
- }
- });
+ config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
+ var env = hostingContext.HostingEnvironment;
+ config.AddJsonFile("appsettings.json", true, false)
+ .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, false);
+ config.AddJsonFile(_ocelotConfigFileName, false, false);
+ config.AddEnvironmentVariables();
+ })
+ .ConfigureServices(s =>
+ {
+ s.AddOcelot()
+ .AddCacheManager((x) =>
+ {
+ x.WithMicrosoftLogging(_ =>
+ {
+ //log.AddConsole(LogLevel.Debug);
+ })
+ .WithJsonSerializer()
+ .WithHandle(typeof(InMemoryJsonHandle<>));
+ });
+ })
+ .Configure(app => { app.UseOcelot().Wait(); });
- result.ShouldBeTrue();
- }
+ _ocelotServer = new TestServer(_webHostBuilder);
- public void GivenOcelotIsRunningUsingJsonSerializedCache()
- {
- _webHostBuilder = new WebHostBuilder();
+ _ocelotClient = _ocelotServer.CreateClient();
+ }
- _webHostBuilder
- .ConfigureAppConfiguration((hostingContext, config) =>
- {
- config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
- var env = hostingContext.HostingEnvironment;
- config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
- .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
- config.AddJsonFile(_ocelotConfigFileName, false, false);
- config.AddEnvironmentVariables();
- })
- .ConfigureServices(s =>
- {
- s.AddOcelot()
- .AddCacheManager((x) =>
- {
- x.WithMicrosoftLogging(log =>
- {
- //log.AddConsole(LogLevel.Debug);
- })
- .WithJsonSerializer()
- .WithHandle(typeof(InMemoryJsonHandle<>));
- });
- })
- .Configure(app =>
- {
- app.UseOcelot().Wait();
- });
+ public void GivenOcelotIsRunningWithFakeHttpClientCache(IHttpClientCache cache)
+ {
+ _webHostBuilder = new WebHostBuilder();
- _ocelotServer = new TestServer(_webHostBuilder);
+ _webHostBuilder
+ .ConfigureAppConfiguration((hostingContext, config) =>
+ {
+ config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
+ var env = hostingContext.HostingEnvironment;
+ config.AddJsonFile("appsettings.json", true, false)
+ .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, false);
+ config.AddJsonFile(_ocelotConfigFileName, false, false);
+ config.AddEnvironmentVariables();
+ })
+ .ConfigureServices(s =>
+ {
+ s.AddSingleton(cache);
+ s.AddOcelot();
+ })
+ .Configure(app => { app.UseOcelot().Wait(); });
- _ocelotClient = _ocelotServer.CreateClient();
- }
+ _ocelotServer = new TestServer(_webHostBuilder);
- public void GivenOcelotIsRunningWithFakeHttpClientCache(IHttpClientCache cache)
- {
- _webHostBuilder = new WebHostBuilder();
+ _ocelotClient = _ocelotServer.CreateClient();
+ }
- _webHostBuilder
- .ConfigureAppConfiguration((hostingContext, config) =>
- {
- config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
- var env = hostingContext.HostingEnvironment;
- config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
- .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
- config.AddJsonFile(_ocelotConfigFileName, false, false);
- config.AddEnvironmentVariables();
- })
- .ConfigureServices(s =>
- {
- s.AddSingleton(cache);
- s.AddOcelot();
- })
- .Configure(app =>
- {
- app.UseOcelot().Wait();
- });
+ internal void GivenIWait(int wait)
+ {
+ Thread.Sleep(wait);
+ }
- _ocelotServer = new TestServer(_webHostBuilder);
+ public void GivenOcelotIsRunningWithMiddlewareBeforePipeline(Func