diff --git a/.circleci/config.yml b/.circleci/config.yml
index e6944a970..4a5f1d936 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -1,29 +1,43 @@
version: 2.1
+orbs:
+ queue: eddiewebb/queue@2.2.1
jobs:
build:
docker:
- - image: mijitt0m/ocelot-build:0.0.1
+ - image: mijitt0m/ocelot-build:0.0.9
steps:
- checkout
- - run: make build
+ - run: dotnet tool restore && dotnet cake
release:
docker:
- - image: mijitt0m/ocelot-build:0.0.1
+ - image: mijitt0m/ocelot-build:0.0.9
steps:
- checkout
- - run: make release
+ - run: dotnet tool restore && dotnet cake --target=Release
workflows:
version: 2
- master:
+ main:
jobs:
+ - queue/block_workflow:
+ time: "20"
+ only-on-branch: main
- release:
+ requires:
+ - queue/block_workflow
filters:
branches:
- only: master
+ only: main
+ develop:
+ jobs:
+ - build:
+ filters:
+ branches:
+ only: develop
pr:
jobs:
- build:
filters:
branches:
ignore:
- - master
+ - main
+ - develop
diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
new file mode 100644
index 000000000..ce3369c2e
--- /dev/null
+++ b/.config/dotnet-tools.json
@@ -0,0 +1,18 @@
+{
+ "version": 1,
+ "isRoot": true,
+ "tools": {
+ "cake.tool": {
+ "version": "3.0.0",
+ "commands": [
+ "dotnet-cake"
+ ]
+ },
+ "coveralls.net": {
+ "version": "4.0.1",
+ "commands": [
+ "csmacnz.Coveralls"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/.dockerignore b/.dockerignore
index 54fec4f0a..aee45d8a2 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,2 +1,5 @@
-*/*/bin
-*/*/obj
+*/*/bin
+*/*/obj
+artifacts/
+TestResults/
+tools/
\ No newline at end of file
diff --git a/.editorconfig b/.editorconfig
index 824353039..6aa3d30f4 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -4,6 +4,7 @@ root = true
end_of_line = lf
insert_final_newline = true
-[*.cs]
+[*.cs]
+end_of_line = lf
indent_style = space
indent_size = 4
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 000000000..167f8eda1
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,5 @@
+# 2010
+*.txt -crlf
+
+# 2020
+*.txt text eol=lf
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 4980a426c..220172830 100644
--- a/.gitignore
+++ b/.gitignore
@@ -183,7 +183,7 @@ ClientBin/
*.dbmdl
*.dbproj.schemaview
*.pfx
-!idsrv3test.pfx
+!mycert.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
diff --git a/Makefile b/Makefile
deleted file mode 100644
index a24d2a923..000000000
--- a/Makefile
+++ /dev/null
@@ -1,20 +0,0 @@
-NAME ?= ocelot
-
-build:
- ./build.sh
-
-build_and_run_tests:
- ./build.sh --target=RunTests
-
-release:
- ./build.sh --target=Release
-
-run_acceptance_tests:
- ./build.sh --target=RunAcceptanceTests
-
-run_benchmarks:
- ./build.sh --target=RunBenchmarkTests
-
-run_unit_tests:
- ./build.sh --target=RunUnitTests
-
\ No newline at end of file
diff --git a/Ocelot.sln b/Ocelot.sln
index 603690596..b59c188a2 100644
--- a/Ocelot.sln
+++ b/Ocelot.sln
@@ -1,21 +1,23 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.29613.14
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.32112.339
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5CFB79B7-C9DC-45A4-9A75-625D92471702}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3FA7C349-DBE8-4904-A2CE-015B8869CE6C}"
ProjectSection(SolutionItems) = preProject
.dockerignore = .dockerignore
+ .editorconfig = .editorconfig
.gitignore = .gitignore
build.cake = build.cake
build.ps1 = build.ps1
codeanalysis.ruleset = codeanalysis.ruleset
+ .circleci\config.yml = .circleci\config.yml
GitVersion.yml = GitVersion.yml
LICENSE.md = LICENSE.md
README.md = README.md
- ReleaseNotes.md = ReleaseNotes.md
+ releasenotes.md = releasenotes.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{5B401523-36DA-4491-B73A-7590A26E420B}"
@@ -42,15 +44,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Eureka", "s
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Polly", "src\Ocelot.Provider.Polly\Ocelot.Provider.Polly.csproj", "{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Rafty", "src\Ocelot.Provider.Rafty\Ocelot.Provider.Rafty.csproj", "{AC153C67-EF18-47E6-A230-F0D3CF5F0A98}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Tracing.Butterfly", "src\Ocelot.Tracing.Butterfly\Ocelot.Tracing.Butterfly.csproj", "{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Kubernetes", "src\Ocelot.Provider.Kubernetes\Ocelot.Provider.Kubernetes.csproj", "{72C8E528-B4F5-45CE-8A06-CD3787364856}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{8FA0CBA0-0338-48EB-B37F-83CA5022237C}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OcelotBasic", "samples\OcelotBasic\OcelotBasic.csproj", "{ED0B3A09-112B-4BA4-82D6-11569BC7A99B}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.OcelotBasic.ApiGateway", "samples\OcelotBasic\Ocelot.Samples.OcelotBasic.ApiGateway.csproj", "{ED0B3A09-112B-4BA4-82D6-11569BC7A99B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AdministrationApi", "samples\AdministrationApi\AdministrationApi.csproj", "{B180F8AE-2F8F-44F9-9E5D-FE65B84B742E}"
EndProject
@@ -64,9 +64,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DownstreamService", "sample
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "k8s", "k8s", "{4B706988-4817-43A8-ABE1-32A67998C2C8}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApiGateway", "samples\OcelotKube\ApiGateway\ApiGateway.csproj", "{8500055B-2C51-4CF1-A6EE-F05BB3E9BF16}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.OcelotKube.ApiGateway", "samples\OcelotKube\ApiGateway\Ocelot.Samples.OcelotKube.ApiGateway.csproj", "{8500055B-2C51-4CF1-A6EE-F05BB3E9BF16}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DownstreamService", "samples\OcelotKube\DownstreamService\DownstreamService.csproj", "{7B319B8C-8155-4779-BD93-5ABD05CA2AB6}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.OcelotKube.DownstreamService", "samples\OcelotKube\DownstreamService\Ocelot.Samples.OcelotKube.DownstreamService.csproj", "{7B319B8C-8155-4779-BD93-5ABD05CA2AB6}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "service-fabric", "service-fabric", "{B412628F-C325-47E1-A8D9-873DE04C8AF5}"
EndProject
@@ -80,6 +80,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "basic", "basic", "{ED066001
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "graphql", "graphql", "{C15CD120-5F8D-41DE-9B21-00E3EA77D6C1}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Tracing.OpenTracing", "src\Ocelot.Tracing.OpenTracing\Ocelot.Tracing.OpenTracing.csproj", "{11C622AD-8C0A-4CF4-811B-3DBB76550797}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "open-tracing", "open-tracing", "{731C6A8A-69ED-445C-A132-C638AA93F9C7}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OcelotOpenTracing", "samples\OcelotOpenTracing\OcelotOpenTracing.csproj", "{C9427E78-4281-4F59-A66E-17C0B66550E5}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -130,10 +136,6 @@ Global
{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Release|Any CPU.Build.0 = Release|Any CPU
- {AC153C67-EF18-47E6-A230-F0D3CF5F0A98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {AC153C67-EF18-47E6-A230-F0D3CF5F0A98}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {AC153C67-EF18-47E6-A230-F0D3CF5F0A98}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {AC153C67-EF18-47E6-A230-F0D3CF5F0A98}.Release|Any CPU.Build.0 = Release|Any CPU
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -178,6 +180,14 @@ Global
{33BE6D88-F188-4E60-83AC-3C4B94D24675}.Debug|Any CPU.Build.0 = Debug|Any CPU
{33BE6D88-F188-4E60-83AC-3C4B94D24675}.Release|Any CPU.ActiveCfg = Release|Any CPU
{33BE6D88-F188-4E60-83AC-3C4B94D24675}.Release|Any CPU.Build.0 = Release|Any CPU
+ {11C622AD-8C0A-4CF4-811B-3DBB76550797}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {11C622AD-8C0A-4CF4-811B-3DBB76550797}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {11C622AD-8C0A-4CF4-811B-3DBB76550797}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {11C622AD-8C0A-4CF4-811B-3DBB76550797}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C9427E78-4281-4F59-A66E-17C0B66550E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C9427E78-4281-4F59-A66E-17C0B66550E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C9427E78-4281-4F59-A66E-17C0B66550E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C9427E78-4281-4F59-A66E-17C0B66550E5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -194,7 +204,6 @@ Global
{02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
{9BBD3586-145C-4FA0-91C5-9ED58287D753} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
- {AC153C67-EF18-47E6-A230-F0D3CF5F0A98} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
{72C8E528-B4F5-45CE-8A06-CD3787364856} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
{ED0B3A09-112B-4BA4-82D6-11569BC7A99B} = {ED066001-BAF7-4117-9884-DF591A56347D}
@@ -212,6 +221,9 @@ Global
{1F1F324D-6EA4-4E63-A6A7-C6053F412F1A} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
{ED066001-BAF7-4117-9884-DF591A56347D} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
{C15CD120-5F8D-41DE-9B21-00E3EA77D6C1} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
+ {11C622AD-8C0A-4CF4-811B-3DBB76550797} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
+ {731C6A8A-69ED-445C-A132-C638AA93F9C7} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
+ {C9427E78-4281-4F59-A66E-17C0B66550E5} = {731C6A8A-69ED-445C-A132-C638AA93F9C7}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {21476EFF-778A-4F97-8A56-D1AF1CEC0C48}
diff --git a/README.md b/README.md
index a655d3d28..bfbafbb48 100644
--- a/README.md
+++ b/README.md
@@ -1,35 +1,21 @@
-[
](https://threemammals.com/ocelot)
+
-[](https://circleci.com/gh/ThreeMammals/Ocelot/tree/master)
+[](https://circleci.com/gh/ThreeMammals/Ocelot/tree/main)
-[](https://coveralls.io/github/ThreeMammals/Ocelot?branch=master)
-
-[Slack](threemammals.slack.com)
+[](https://coveralls.io/github/ThreeMammals/Ocelot)
# Ocelot
-Ocelot is a .NET API Gateway. This project is aimed at people using .NET running
-a micro services / service oriented architecture
+Ocelot is a .NET API Gateway. This project is aimed at people using .NET running a micro services / service oriented architecture
that need a unified point of entry into their system. However it will work with anything that speaks HTTP and run on any platform that ASP.NET Core supports.
-In particular I want easy integration with
-IdentityServer reference and bearer tokens.
+In particular I want easy integration with IdentityServer reference and bearer tokens.
-We have been unable to find this in my current workplace
-without having to write our own Javascript middlewares
-to handle the IdentityServer reference tokens. We would
-rather use the IdentityServer code that already exists
-to do this.
+We have been unable to find this in my current workplace without having to write our own Javascript middlewares to handle the IdentityServer reference tokens. We would rather use the IdentityServer code that already exists to do this.
Ocelot is a bunch of middlewares in a specific order.
-Ocelot manipulates the HttpRequest object into a state specified by its configuration until
-it reaches a request builder middleware where it creates a HttpRequestMessage object which is
-used to make a request to a downstream service. The middleware that makes the request is
-the last thing in the Ocelot pipeline. It does not call the next middleware.
-The response from the downstream service is retrieved as the requests goes back up the Ocelot pipeline.
-There is a piece of middleware that maps the HttpResponseMessage onto the HttpResponse object and that
-is returned to the client. That is basically it with a bunch of other features!
+Ocelot manipulates the HttpRequest object into a state specified by its configuration until it reaches a request builder middleware where it creates a HttpRequestMessage object which is used to make a request to a downstream service. The middleware that makes the request is the last thing in the Ocelot pipeline. It does not call the next middleware. The response from the downstream service is retrieved as the requests goes back up the Ocelot pipeline. There is a piece of middleware that maps the HttpResponseMessage onto the HttpResponse object and that is returned to the client. That is basically it with a bunch of other features!
## Features
@@ -42,22 +28,22 @@ A quick list of Ocelot's capabilities for more information see the [documentatio
* Kubernetes
* WebSockets
* Authentication
-* Authorisation
+* Authorization
* Rate Limiting
* Caching
* Retry policies / QoS
* Load Balancing
* Logging / Tracing / Correlation
-* Headers / Query String / Claims Transformation
+* Headers / Method / Query String / Claims Transformation
* Custom Middleware / Delegating Handlers
* Configuration / Administration REST API
* Platform / Cloud Agnostic
## How to install
-Ocelot is designed to work with ASP.NET Core only and it targets `netstandard2.0`. This means it can be used anywhere `.NET Standard 2.0` is supported, including `.NET Core 3.1` and `.NET Framework 4.8` and up. [This](https://docs.microsoft.com/en-us/dotnet/standard/net-standard) documentation may prove helpful when working out if Ocelot would be suitable for you.
+Ocelot is designed to work with ASP.NET and it targets `net7.0`.
-Install Ocelot and it's dependencies using NuGet.
+Install Ocelot and it's dependencies using NuGet.
`Install-Package Ocelot`
@@ -81,18 +67,16 @@ We love to receive contributions from the community so please keep them coming :
Pull requests, issues and commentary welcome!
-Please complete the relevant template for issues and PRs. Sometimes it's worth getting in touch with us to discuss changes
-before doing any work incase this is something we are already doing or it might not make sense. We can also give
-advice on the easiest way to do things :)
+Please complete the relevant template for issues and PRs. Sometimes it's worth getting in touch with us to discuss changes before doing any work incase this is something we are already doing or it might not make sense. We can also give advice on the easiest way to do things :)
Finally we mark all existing issues as help wanted, small, medium and large effort. If you want to contribute for the first time I suggest looking at a help wanted & small effort issue :)
## Donate
-If you think this project is worth supporting financially please make a contribution using the button below!
+If you think this project is worth supporting financially please make a contribution using the button below! We use the money to run the https://threemammals.com website.
[](https://www.paypal.me/ThreeMammals/)
## Things that are currently annoying me
-[ Get more details at **codescene.io**.](https://codescene.io/projects/697/jobs/latest-successful/results)
+[ Get more details at **codescene.io**.](https://codescene.io/projects/697/jobs/latest-successful/results)
\ No newline at end of file
diff --git a/ReleaseNotes.md b/ReleaseNotes.md
deleted file mode 100644
index a647c6c30..000000000
--- a/ReleaseNotes.md
+++ /dev/null
@@ -1 +0,0 @@
-No issues closed since last release
\ No newline at end of file
diff --git a/build.cake b/build.cake
index 3d4626bdb..67d144808 100644
--- a/build.cake
+++ b/build.cake
@@ -1,500 +1,573 @@
-#tool "nuget:?package=GitVersion.CommandLine&version=5.0.1"
-#tool "nuget:?package=GitReleaseNotes"
-#addin nuget:?package=Cake.Json
-#addin nuget:?package=Newtonsoft.Json
-#addin nuget:?package=System.Net.Http
-#tool "nuget:?package=ReportGenerator"
-#tool "nuget:?package=coveralls.net&version=0.7.0"
-#addin Cake.Coveralls&version=0.10.1
-
-// compile
-var compileConfig = Argument("configuration", "Release");
-
-var slnFile = "./Ocelot.sln";
-
-// build artifacts
-var artifactsDir = Directory("artifacts");
-
-// unit testing
-var artifactsForUnitTestsDir = artifactsDir + Directory("UnitTests");
-var unitTestAssemblies = @"./test/Ocelot.UnitTests/Ocelot.UnitTests.csproj";
-var minCodeCoverage = 80d;
-var coverallsRepoToken = "OCELOT_COVERALLS_TOKEN";
-var coverallsRepo = "https://coveralls.io/github/ThreeMammals/Ocelot";
-
-// acceptance testing
-var artifactsForAcceptanceTestsDir = artifactsDir + Directory("AcceptanceTests");
-var acceptanceTestAssemblies = @"./test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj";
-
-// integration testing
-var artifactsForIntegrationTestsDir = artifactsDir + Directory("IntegrationTests");
-var integrationTestAssemblies = @"./test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj";
-
-// benchmark testing
-var artifactsForBenchmarkTestsDir = artifactsDir + Directory("BenchmarkTests");
-var benchmarkTestAssemblies = @"./test/Ocelot.Benchmarks";
-
-// packaging
-var packagesDir = artifactsDir + Directory("Packages");
-var releaseNotesFile = packagesDir + File("releasenotes.md");
-var artifactsFile = packagesDir + File("artifacts.txt");
-
-// stable releases
-var tagsUrl = "https://api.github.com/repos/ThreeMammals/ocelot/releases/tags/";
-var nugetFeedStableKey = EnvironmentVariable("OCELOT_NUTGET_API_KEY");
-var nugetFeedStableUploadUrl = "https://www.nuget.org/api/v2/package";
-var nugetFeedStableSymbolsUploadUrl = "https://www.nuget.org/api/v2/package";
-
-// internal build variables - don't change these.
-string committedVersion = "0.0.0-dev";
-GitVersion versioning = null;
-int releaseId = 0;
-string gitHubUsername = "TomPallister";
-string gitHubPassword = Environment.GetEnvironmentVariable("OCELOT_GITHUB_API_KEY");
-
-var target = Argument("target", "Default");
-
-Information("target is " + target);
-Information("Build configuration is " + compileConfig);
-
-Task("Default")
- .IsDependentOn("Build");
-
-Task("Build")
- .IsDependentOn("RunTests");
-
-Task("RunTests")
- .IsDependentOn("RunUnitTests")
- .IsDependentOn("RunAcceptanceTests")
- .IsDependentOn("RunIntegrationTests");
-
-Task("Release")
- .IsDependentOn("Build")
- .IsDependentOn("CreateArtifacts")
- .IsDependentOn("PublishGitHubRelease")
- .IsDependentOn("PublishToNuget");
-
-Task("Compile")
- .IsDependentOn("Clean")
- .IsDependentOn("Version")
- .Does(() =>
- {
- var settings = new DotNetCoreBuildSettings
- {
- Configuration = compileConfig,
- };
-
- DotNetCoreBuild(slnFile, settings);
- });
-
-Task("Clean")
- .Does(() =>
- {
- if (DirectoryExists(artifactsDir))
- {
- DeleteDirectory(artifactsDir, recursive:true);
- }
- CreateDirectory(artifactsDir);
- });
-
-Task("Version")
- .Does(() =>
- {
- versioning = GetNuGetVersionForCommit();
- var nugetVersion = versioning.NuGetVersion;
- Information("SemVer version number: " + nugetVersion);
-
- if (IsRunningOnCircleCI())
- {
- Information("Persisting version number...");
- PersistVersion(committedVersion, nugetVersion);
- }
- else
- {
- Information("We are not running on build server, so we won't persist the version number.");
- }
- });
-
-Task("RunUnitTests")
- .IsDependentOn("Compile")
- .Does(() =>
- {
- var testSettings = new DotNetCoreTestSettings
- {
- Configuration = compileConfig,
- ResultsDirectory = artifactsForUnitTestsDir,
- ArgumentCustomization = args => args
- // this create the code coverage report
- .Append("--settings test/Ocelot.UnitTests/UnitTests.runsettings")
- };
-
- EnsureDirectoryExists(artifactsForUnitTestsDir);
- DotNetCoreTest(unitTestAssemblies, testSettings);
-
- var coverageSummaryFile = GetSubDirectories(artifactsForUnitTestsDir).First().CombineWithFilePath(File("coverage.opencover.xml"));
- Information(coverageSummaryFile);
- Information(artifactsForUnitTestsDir);
- // todo bring back report generator to get a friendly report
- // ReportGenerator(coverageSummaryFile, artifactsForUnitTestsDir);
- // https://github.com/danielpalme/ReportGenerator
-
- if (IsRunningOnCircleCI())
- {
- var repoToken = EnvironmentVariable(coverallsRepoToken);
- if (string.IsNullOrEmpty(repoToken))
- {
- throw new Exception(string.Format("Coveralls repo token not found. Set environment variable '{0}'", coverallsRepoToken));
- }
-
- Information(string.Format("Uploading test coverage to {0}", coverallsRepo));
- CoverallsNet(coverageSummaryFile, CoverallsNetReportType.OpenCover, new CoverallsNetSettings()
- {
- RepoToken = repoToken
- });
- }
- else
- {
- Information("We are not running on the build server so we won't publish the coverage report to coveralls.io");
- }
-
- var sequenceCoverage = XmlPeek(coverageSummaryFile, "//CoverageSession/Summary/@sequenceCoverage");
- var branchCoverage = XmlPeek(coverageSummaryFile, "//CoverageSession/Summary/@branchCoverage");
-
- Information("Sequence Coverage: " + sequenceCoverage);
-
- if(double.Parse(sequenceCoverage) < minCodeCoverage)
- {
- var whereToCheck = !IsRunningOnCircleCI() ? coverallsRepo : artifactsForUnitTestsDir;
- throw new Exception(string.Format("Code coverage fell below the threshold of {0}%. You can find the code coverage report at {1}", minCodeCoverage, whereToCheck));
- };
- });
-
-Task("RunAcceptanceTests")
- .IsDependentOn("Compile")
- .Does(() =>
- {
- var settings = new DotNetCoreTestSettings
- {
- Configuration = compileConfig,
- ArgumentCustomization = args => args
- .Append("--no-restore")
- .Append("--no-build")
- };
-
- EnsureDirectoryExists(artifactsForAcceptanceTestsDir);
- DotNetCoreTest(acceptanceTestAssemblies, settings);
- });
-
-Task("RunIntegrationTests")
- .IsDependentOn("Compile")
- .Does(() =>
- {
- var settings = new DotNetCoreTestSettings
- {
- Configuration = compileConfig,
- ArgumentCustomization = args => args
- .Append("--no-restore")
- .Append("--no-build")
- };
-
- EnsureDirectoryExists(artifactsForIntegrationTestsDir);
- DotNetCoreTest(integrationTestAssemblies, settings);
- });
-
-Task("CreateArtifacts")
- .IsDependentOn("Compile")
- .Does(() =>
- {
- EnsureDirectoryExists(packagesDir);
-
- CopyFiles("./src/**/Release/Ocelot.*.nupkg", packagesDir);
-
- // todo fix this for docker build
- //GenerateReleaseNotes(releaseNotesFile);
-
- var projectFiles = GetFiles("./src/**/Release/Ocelot.*.nupkg");
-
- foreach(var projectFile in projectFiles)
- {
- System.IO.File.AppendAllLines(artifactsFile, new[]{
- projectFile.GetFilename().FullPath,
- // todo fix this for docker build
- //"releaseNotes:releasenotes.md"
- });
- }
-
- var artifacts = System.IO.File
- .ReadAllLines(artifactsFile)
- .Distinct();
-
- foreach(var artifact in artifacts)
- {
- var codePackage = packagesDir + File(artifact);
-
- Information("Created package " + codePackage);
- }
- });
-
-Task("PublishGitHubRelease")
- .IsDependentOn("CreateArtifacts")
- .Does(() =>
- {
- if (IsRunningOnCircleCI())
- {
- var path = packagesDir.ToString() + @"/**/*";
-
- CreateGitHubRelease();
-
- foreach (var file in GetFiles(path))
- {
- UploadFileToGitHubRelease(file);
- }
-
- CompleteGitHubRelease();
- }
- });
-
-Task("EnsureStableReleaseRequirements")
- .Does(() =>
- {
- Information("Check if stable release...");
-
- if (!IsRunningOnCircleCI())
- {
- throw new Exception("Stable release should happen via circleci");
- }
-
- Information("Release is stable...");
- });
-
-Task("DownloadGitHubReleaseArtifacts")
- .Does(() =>
- {
-
- try
- {
- EnsureDirectoryExists(packagesDir);
-
- var releaseUrl = tagsUrl + versioning.NuGetVersion;
-
- var assets_url = Newtonsoft.Json.Linq.JObject.Parse(GetResource(releaseUrl))
- .Value("assets_url");
-
- var assets = GetResource(assets_url);
-
- foreach(var asset in Newtonsoft.Json.JsonConvert.DeserializeObject(assets))
- {
- var file = packagesDir + File(asset.Value("name"));
- DownloadFile(asset.Value("browser_download_url"), file);
- }
- }
- catch(Exception exception)
- {
- Information("There was an exception " + exception);
- throw;
- }
- });
-
-Task("PublishToNuget")
- .IsDependentOn("DownloadGitHubReleaseArtifacts")
- .Does(() =>
- {
- if (IsRunningOnCircleCI())
- {
- PublishPackages(packagesDir, artifactsFile, nugetFeedStableKey, nugetFeedStableUploadUrl, nugetFeedStableSymbolsUploadUrl);
- }
- });
-
-RunTarget(target);
-
-/// Gets nuique nuget version for this commit
-private GitVersion GetNuGetVersionForCommit()
-{
- GitVersion(new GitVersionSettings{
- UpdateAssemblyInfo = false,
- OutputType = GitVersionOutput.BuildServer
- });
-
- return GitVersion(new GitVersionSettings{ OutputType = GitVersionOutput.Json });
-}
-
-/// Updates project version in all of our projects
-private void PersistVersion(string committedVersion, string newVersion)
-{
- Information(string.Format("We'll search all csproj files for {0} and replace with {1}...", committedVersion, newVersion));
-
- var projectFiles = GetFiles("./**/*.csproj");
-
- foreach(var projectFile in projectFiles)
- {
- var file = projectFile.ToString();
-
- Information(string.Format("Updating {0}...", file));
-
- var updatedProjectFile = System.IO.File.ReadAllText(file)
- .Replace(committedVersion, newVersion);
-
- System.IO.File.WriteAllText(file, updatedProjectFile);
- }
-}
-
-/// generates release notes based on issues closed in GitHub since the last release
-private void GenerateReleaseNotes(ConvertableFilePath file)
-{
- if(!IsRunningOnWindows())
- {
- Warning("We are not running on Windows so we cannot generate release notes.");
- return;
- }
-
- Information("Generating release notes at " + file);
-
- var releaseNotesExitCode = StartProcess(
- @"tools/GitReleaseNotes/tools/gitreleasenotes.exe",
- new ProcessSettings { Arguments = ". /o " + file });
-
- if (string.IsNullOrEmpty(System.IO.File.ReadAllText(file)))
- {
- System.IO.File.WriteAllText(file, "No issues closed since last release");
- }
-
- if (releaseNotesExitCode != 0)
- {
- throw new Exception("Failed to generate release notes");
- }
-}
-
-/// Publishes code and symbols packages to nuget feed, based on contents of artifacts file
-private void PublishPackages(ConvertableDirectoryPath packagesDir, ConvertableFilePath artifactsFile, string feedApiKey, string codeFeedUrl, string symbolFeedUrl)
-{
- Information("PublishPackages");
- var artifacts = System.IO.File
- .ReadAllLines(artifactsFile)
- .Distinct();
-
- foreach(var artifact in artifacts)
- {
- var codePackage = packagesDir + File(artifact);
-
- Information("Pushing package " + codePackage);
-
- Information("Calling NuGetPush");
-
- NuGetPush(
- codePackage,
- new NuGetPushSettings {
- ApiKey = feedApiKey,
- Source = codeFeedUrl
- });
- }
-}
-
-private void CreateGitHubRelease()
-{
- var json = $"{{ \"tag_name\": \"{versioning.NuGetVersion}\", \"target_commitish\": \"master\", \"name\": \"{versioning.NuGetVersion}\", \"body\": \"todo: notes coming\", \"draft\": true, \"prerelease\": true }}";
- var content = new System.Net.Http.StringContent(json, System.Text.Encoding.UTF8, "application/json");
-
- using(var client = new System.Net.Http.HttpClient())
- {
- client.DefaultRequestHeaders.Authorization =
- new System.Net.Http.Headers.AuthenticationHeaderValue(
- "Basic", Convert.ToBase64String(
- System.Text.ASCIIEncoding.ASCII.GetBytes(
- $"{gitHubUsername}:{gitHubPassword}")));
-
- client.DefaultRequestHeaders.Add("User-Agent", "Ocelot Release");
-
- var result = client.PostAsync("https://api.github.com/repos/ThreeMammals/Ocelot/releases", content).Result;
- if(result.StatusCode != System.Net.HttpStatusCode.Created)
- {
- throw new Exception("CreateGitHubRelease result.StatusCode = " + result.StatusCode);
- }
- var returnValue = result.Content.ReadAsStringAsync().Result;
- dynamic test = Newtonsoft.Json.JsonConvert.DeserializeObject(returnValue);
- releaseId = test.id;
- }
-}
-
-private void UploadFileToGitHubRelease(FilePath file)
-{
- var data = System.IO.File.ReadAllBytes(file.FullPath);
- var content = new System.Net.Http.ByteArrayContent(data);
- content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
-
- using(var client = new System.Net.Http.HttpClient())
- {
- client.DefaultRequestHeaders.Authorization =
- new System.Net.Http.Headers.AuthenticationHeaderValue(
- "Basic", Convert.ToBase64String(
- System.Text.ASCIIEncoding.ASCII.GetBytes(
- $"{gitHubUsername}:{gitHubPassword}")));
-
- client.DefaultRequestHeaders.Add("User-Agent", "Ocelot Release");
-
- var result = client.PostAsync($"https://uploads.github.com/repos/ThreeMammals/Ocelot/releases/{releaseId}/assets?name={file.GetFilename()}", content).Result;
- if(result.StatusCode != System.Net.HttpStatusCode.Created)
- {
- throw new Exception("UploadFileToGitHubRelease result.StatusCode = " + result.StatusCode);
- }
- }
-}
-
-private void CompleteGitHubRelease()
-{
- var json = $"{{ \"tag_name\": \"{versioning.NuGetVersion}\", \"target_commitish\": \"master\", \"name\": \"{versioning.NuGetVersion}\", \"body\": \"todo: notes coming\", \"draft\": false, \"prerelease\": false }}";
- var request = new System.Net.Http.HttpRequestMessage(new System.Net.Http.HttpMethod("Patch"), $"https://api.github.com/repos/ThreeMammals/Ocelot/releases/{releaseId}");
- request.Content = new System.Net.Http.StringContent(json, System.Text.Encoding.UTF8, "application/json");
-
- using(var client = new System.Net.Http.HttpClient())
- {
- client.DefaultRequestHeaders.Authorization =
- new System.Net.Http.Headers.AuthenticationHeaderValue(
- "Basic", Convert.ToBase64String(
- System.Text.ASCIIEncoding.ASCII.GetBytes(
- $"{gitHubUsername}:{gitHubPassword}")));
-
- client.DefaultRequestHeaders.Add("User-Agent", "Ocelot Release");
-
- var result = client.SendAsync(request).Result;
- if(result.StatusCode != System.Net.HttpStatusCode.OK)
- {
- throw new Exception("CompleteGitHubRelease result.StatusCode = " + result.StatusCode);
- }
- }
-}
-
-
-/// gets the resource from the specified url
-private string GetResource(string url)
-{
- try
- {
- Information("Getting resource from " + url);
-
- var assetsRequest = System.Net.WebRequest.CreateHttp(url);
- assetsRequest.Method = "GET";
- assetsRequest.Accept = "application/vnd.github.v3+json";
- assetsRequest.UserAgent = "BuildScript";
-
- using (var assetsResponse = assetsRequest.GetResponse())
- {
- var assetsStream = assetsResponse.GetResponseStream();
- var assetsReader = new StreamReader(assetsStream);
- var response = assetsReader.ReadToEnd();
-
- Information("Response is " + response);
-
- return response;
- }
- }
- catch(Exception exception)
- {
- Information("There was an exception " + exception);
- throw;
- }
-}
-
-private bool IsRunningOnCircleCI()
-{
- return !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("CIRCLECI"));
+#tool "dotnet:?package=GitVersion.Tool&version=5.8.1"
+#tool "dotnet:?package=coveralls.net&version=4.0.1"
+#addin nuget:?package=Newtonsoft.Json
+#addin nuget:?package=System.Text.Encodings.Web&version=4.7.1
+#tool "nuget:?package=ReportGenerator&version=5.1.19"
+#addin Cake.Coveralls&version=1.1.0
+
+// compile
+var compileConfig = Argument("configuration", "Release");
+
+var slnFile = "./Ocelot.sln";
+
+// build artifacts
+var artifactsDir = Directory("artifacts");
+
+// unit testing
+var artifactsForUnitTestsDir = artifactsDir + Directory("UnitTests");
+var unitTestAssemblies = @"./test/Ocelot.UnitTests/Ocelot.UnitTests.csproj";
+var minCodeCoverage = 0.80d;
+var coverallsRepoToken = "OCELOT_COVERALLS_TOKEN";
+var coverallsRepo = "https://coveralls.io/github/ThreeMammals/Ocelot";
+
+// acceptance testing
+var artifactsForAcceptanceTestsDir = artifactsDir + Directory("AcceptanceTests");
+var acceptanceTestAssemblies = @"./test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj";
+
+// integration testing
+var artifactsForIntegrationTestsDir = artifactsDir + Directory("IntegrationTests");
+var integrationTestAssemblies = @"./test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj";
+
+// benchmark testing
+var artifactsForBenchmarkTestsDir = artifactsDir + Directory("BenchmarkTests");
+var benchmarkTestAssemblies = @"./test/Ocelot.Benchmarks";
+
+// packaging
+var packagesDir = artifactsDir + Directory("Packages");
+var releaseNotesFile = packagesDir + File("releasenotes.md");
+var artifactsFile = packagesDir + File("artifacts.txt");
+
+// stable releases
+var tagsUrl = "https://api.github.com/repos/ThreeMammals/ocelot/releases/tags/";
+var nugetFeedStableKey = EnvironmentVariable("OCELOT_NUTGET_API_KEY");
+var nugetFeedStableUploadUrl = "https://www.nuget.org/api/v2/package";
+var nugetFeedStableSymbolsUploadUrl = "https://www.nuget.org/api/v2/package";
+
+// internal build variables - don't change these.
+string committedVersion = "0.0.0-dev";
+GitVersion versioning = null;
+int releaseId = 0;
+string gitHubUsername = "TomPallister";
+string gitHubPassword = Environment.GetEnvironmentVariable("OCELOT_GITHUB_API_KEY");
+
+var target = Argument("target", "Default");
+
+Information("target is " + target);
+Information("Build configuration is " + compileConfig);
+
+Task("Default")
+ .IsDependentOn("Build");
+
+Task("Build")
+ .IsDependentOn("RunTests");
+
+Task("ReleaseNotes")
+ .IsDependentOn("CreateReleaseNotes");
+
+Task("RunTests")
+ .IsDependentOn("RunUnitTests")
+ .IsDependentOn("RunAcceptanceTests")
+ .IsDependentOn("RunIntegrationTests");
+
+Task("Release")
+ .IsDependentOn("Build")
+ .IsDependentOn("CreateArtifacts")
+ .IsDependentOn("PublishGitHubRelease")
+ .IsDependentOn("PublishToNuget");
+
+Task("Compile")
+ .IsDependentOn("Clean")
+ .IsDependentOn("Version")
+ .Does(() =>
+ {
+ var settings = new DotNetBuildSettings
+ {
+ Configuration = compileConfig,
+ };
+
+ DotNetBuild(slnFile, settings);
+ });
+
+Task("Clean")
+ .Does(() =>
+ {
+ if (DirectoryExists(artifactsDir))
+ {
+ DeleteDirectory(artifactsDir, new DeleteDirectorySettings {
+ Recursive = true,
+ Force = true
+ });
+ }
+ CreateDirectory(artifactsDir);
+ });
+
+Task("CreateReleaseNotes")
+ .Does(() =>
+ {
+ Information("Generating release notes at " + releaseNotesFile);
+
+ IEnumerable lastReleaseTag;
+
+ var lastReleaseTagExitCode = StartProcess(
+ "git",
+ new ProcessSettings {
+ Arguments = "describe --tags --abbrev=0",
+ RedirectStandardOutput = true
+ },
+ out lastReleaseTag
+ );
+
+ if (lastReleaseTagExitCode != 0)
+ {
+ throw new Exception("Failed to get latest release tag");
+ }
+
+ var lastRelease = lastReleaseTag.First();
+
+ Information("Last release tag is " + lastRelease);
+
+ IEnumerable releaseNotes;
+
+ var releaseNotesExitCode = StartProcess(
+ "git",
+ new ProcessSettings {
+ Arguments = $"log --pretty=format:\"%h - %an - %s\" {lastRelease}..HEAD",
+ RedirectStandardOutput = true
+ },
+ out releaseNotes
+ );
+
+ if (releaseNotesExitCode != 0)
+ {
+ throw new Exception("Failed to generate release notes");
+ }
+
+ EnsureDirectoryExists(packagesDir);
+
+ System.IO.File.WriteAllLines(releaseNotesFile, releaseNotes);
+
+ if (string.IsNullOrEmpty(System.IO.File.ReadAllText(releaseNotesFile)))
+ {
+ System.IO.File.WriteAllText(releaseNotesFile, "No commits since last release");
+ }
+
+ Information("Release notes are\r\n" + System.IO.File.ReadAllText(releaseNotesFile));
+ });
+
+Task("Version")
+ .IsDependentOn("CreateReleaseNotes")
+ .Does(() =>
+ {
+ versioning = GetNuGetVersionForCommit();
+ var nugetVersion = versioning.NuGetVersion;
+ Information("SemVer version number: " + nugetVersion);
+
+ if (IsRunningOnCircleCI())
+ {
+ Information("Persisting version number...");
+ PersistVersion(committedVersion, nugetVersion);
+ }
+ else
+ {
+ Information("We are not running on build server, so we won't persist the version number.");
+ }
+ });
+
+Task("RunUnitTests")
+ .IsDependentOn("Compile")
+ .Does(() =>
+ {
+ var testSettings = new DotNetTestSettings
+ {
+ Configuration = compileConfig,
+ ResultsDirectory = artifactsForUnitTestsDir,
+ ArgumentCustomization = args => args
+ // this create the code coverage report
+ .Append("--collect:\"XPlat Code Coverage\"")
+ };
+
+ EnsureDirectoryExists(artifactsForUnitTestsDir);
+ DotNetTest(unitTestAssemblies, testSettings);
+
+ var coverageSummaryFile = GetSubDirectories(artifactsForUnitTestsDir).First().CombineWithFilePath(File("coverage.cobertura.xml"));
+ Information(coverageSummaryFile);
+ Information(artifactsForUnitTestsDir);
+
+ GenerateReport(coverageSummaryFile);
+
+ if (IsRunningOnCircleCI() && IsMainOrDevelop())
+ {
+ var repoToken = EnvironmentVariable(coverallsRepoToken);
+ if (string.IsNullOrEmpty(repoToken))
+ {
+ throw new Exception(string.Format("Coveralls repo token not found. Set environment variable '{0}'", coverallsRepoToken));
+ }
+
+ Information(string.Format("Uploading test coverage to {0}", coverallsRepo));
+ CoverallsNet(coverageSummaryFile, CoverallsNetReportType.OpenCover, new CoverallsNetSettings()
+ {
+ RepoToken = repoToken
+ });
+ }
+ else
+ {
+ Information("We are not running on the build server so we won't publish the coverage report to coveralls.io");
+ }
+
+ var sequenceCoverage = XmlPeek(coverageSummaryFile, "//coverage/@line-rate");
+ var branchCoverage = XmlPeek(coverageSummaryFile, "//coverage/@line-rate");
+
+ Information("Sequence Coverage: " + sequenceCoverage);
+
+ if(double.Parse(sequenceCoverage) < minCodeCoverage)
+ {
+ var whereToCheck = !IsRunningOnCircleCI() ? coverallsRepo : artifactsForUnitTestsDir;
+ throw new Exception(string.Format("Code coverage fell below the threshold of {0}%. You can find the code coverage report at {1}", minCodeCoverage, whereToCheck));
+ };
+ });
+
+Task("RunAcceptanceTests")
+ .IsDependentOn("Compile")
+ .Does(() =>
+ {
+ var settings = new DotNetTestSettings
+ {
+ Configuration = compileConfig,
+ ArgumentCustomization = args => args
+ .Append("--no-restore")
+ .Append("--no-build")
+ };
+
+ EnsureDirectoryExists(artifactsForAcceptanceTestsDir);
+ DotNetTest(acceptanceTestAssemblies, settings);
+ });
+
+Task("RunIntegrationTests")
+ .IsDependentOn("Compile")
+ .Does(() =>
+ {
+ var settings = new DotNetTestSettings
+ {
+ Configuration = compileConfig,
+ ArgumentCustomization = args => args
+ .Append("--no-restore")
+ .Append("--no-build")
+ };
+
+ EnsureDirectoryExists(artifactsForIntegrationTestsDir);
+ DotNetTest(integrationTestAssemblies, settings);
+ });
+
+Task("CreateArtifacts")
+ .IsDependentOn("Compile")
+ .Does(() =>
+ {
+ EnsureDirectoryExists(packagesDir);
+
+ CopyFiles("./src/**/Release/Ocelot.*.nupkg", packagesDir);
+
+ var projectFiles = GetFiles("./src/**/Release/Ocelot.*.nupkg");
+
+ foreach(var projectFile in projectFiles)
+ {
+ System.IO.File.AppendAllLines(artifactsFile, new[]{
+ projectFile.GetFilename().FullPath,
+ "releasenotes.md"
+ });
+ }
+
+ var artifacts = System.IO.File
+ .ReadAllLines(artifactsFile)
+ .Distinct();
+
+ foreach(var artifact in artifacts)
+ {
+ var codePackage = packagesDir + File(artifact);
+
+ Information("Created package " + codePackage);
+ }
+ });
+
+Task("PublishGitHubRelease")
+ .IsDependentOn("CreateArtifacts")
+ .Does(() =>
+ {
+ if (IsRunningOnCircleCI())
+ {
+ var path = packagesDir.ToString() + @"/**/*";
+
+ CreateGitHubRelease();
+
+ foreach (var file in GetFiles(path))
+ {
+ UploadFileToGitHubRelease(file);
+ }
+
+ CompleteGitHubRelease();
+ }
+ });
+
+Task("EnsureStableReleaseRequirements")
+ .Does(() =>
+ {
+ Information("Check if stable release...");
+
+ if (!IsRunningOnCircleCI())
+ {
+ throw new Exception("Stable release should happen via circleci");
+ }
+
+ Information("Release is stable...");
+ });
+
+Task("DownloadGitHubReleaseArtifacts")
+ .Does(() =>
+ {
+
+ try
+ {
+ // hack to let GitHub catch up, todo - refactor to poll
+ System.Threading.Thread.Sleep(5000);
+
+ EnsureDirectoryExists(packagesDir);
+
+ var releaseUrl = tagsUrl + versioning.NuGetVersion;
+
+ var assets_url = Newtonsoft.Json.Linq.JObject.Parse(GetResource(releaseUrl))
+ .Value("assets_url");
+
+ var assets = GetResource(assets_url);
+
+ foreach(var asset in Newtonsoft.Json.JsonConvert.DeserializeObject(assets))
+ {
+ var file = packagesDir + File(asset.Value("name"));
+ DownloadFile(asset.Value("browser_download_url"), file);
+ }
+ }
+ catch(Exception exception)
+ {
+ Information("There was an exception " + exception);
+ throw;
+ }
+ });
+
+Task("PublishToNuget")
+ .IsDependentOn("DownloadGitHubReleaseArtifacts")
+ .Does(() =>
+ {
+ if (IsRunningOnCircleCI())
+ {
+ PublishPackages(packagesDir, artifactsFile, nugetFeedStableKey, nugetFeedStableUploadUrl, nugetFeedStableSymbolsUploadUrl);
+ }
+ });
+
+RunTarget(target);
+
+private void GenerateReport(Cake.Core.IO.FilePath coverageSummaryFile)
+{
+ var dir = System.IO.Directory.GetCurrentDirectory();
+ Information(dir);
+
+ var reportSettings = new ProcessArgumentBuilder();
+ reportSettings.Append($"-targetdir:" + $"{dir}/{artifactsForUnitTestsDir}");
+ reportSettings.Append($"-reports:" + coverageSummaryFile);
+
+ var toolpath = Context.Tools.Resolve("net7.0/ReportGenerator.dll");
+ Information($"Tool Path : {toolpath.ToString()}");
+
+ DotNetExecute(toolpath, reportSettings);
+}
+
+/// Gets unique nuget version for this commit
+private GitVersion GetNuGetVersionForCommit()
+{
+ GitVersion(new GitVersionSettings{
+ UpdateAssemblyInfo = false,
+ OutputType = GitVersionOutput.BuildServer
+ });
+
+ return GitVersion(new GitVersionSettings{ OutputType = GitVersionOutput.Json });
+}
+
+/// Updates project version in all of our projects
+private void PersistVersion(string committedVersion, string newVersion)
+{
+ Information(string.Format("We'll search all csproj files for {0} and replace with {1}...", committedVersion, newVersion));
+
+ var projectFiles = GetFiles("./**/*.csproj");
+
+ foreach(var projectFile in projectFiles)
+ {
+ var file = projectFile.ToString();
+
+ Information(string.Format("Updating {0}...", file));
+
+ var updatedProjectFile = System.IO.File.ReadAllText(file)
+ .Replace(committedVersion, newVersion);
+
+ System.IO.File.WriteAllText(file, updatedProjectFile);
+ }
+}
+
+/// Publishes code and symbols packages to nuget feed, based on contents of artifacts file
+private void PublishPackages(ConvertableDirectoryPath packagesDir, ConvertableFilePath artifactsFile, string feedApiKey, string codeFeedUrl, string symbolFeedUrl)
+{
+ Information("PublishPackages");
+ var artifacts = System.IO.File
+ .ReadAllLines(artifactsFile)
+ .Distinct();
+
+ foreach(var artifact in artifacts)
+ {
+ if (artifact == "releasenotes.md")
+ {
+ continue;
+ }
+
+ var codePackage = packagesDir + File(artifact);
+
+ Information("Pushing package " + codePackage);
+
+ Information("Calling NuGetPush");
+
+ DotNetNuGetPush(
+ codePackage,
+ new DotNetNuGetPushSettings {
+ ApiKey = feedApiKey,
+ Source = codeFeedUrl
+ });
+ }
+}
+
+private void CreateGitHubRelease()
+{
+ var json = $"{{ \"tag_name\": \"{versioning.NuGetVersion}\", \"target_commitish\": \"main\", \"name\": \"{versioning.NuGetVersion}\", \"body\": \"{ReleaseNotesAsJson()}\", \"draft\": true, \"prerelease\": true }}";
+
+ var content = new System.Net.Http.StringContent(json, System.Text.Encoding.UTF8, "application/json");
+
+ using(var client = new System.Net.Http.HttpClient())
+ {
+ client.DefaultRequestHeaders.Authorization =
+ new System.Net.Http.Headers.AuthenticationHeaderValue(
+ "Basic", Convert.ToBase64String(
+ System.Text.ASCIIEncoding.ASCII.GetBytes(
+ $"{gitHubUsername}:{gitHubPassword}")));
+
+ client.DefaultRequestHeaders.Add("User-Agent", "Ocelot Release");
+
+ var result = client.PostAsync("https://api.github.com/repos/ThreeMammals/Ocelot/releases", content).Result;
+ if(result.StatusCode != System.Net.HttpStatusCode.Created)
+ {
+ throw new Exception("CreateGitHubRelease result.StatusCode = " + result.StatusCode);
+ }
+ var returnValue = result.Content.ReadAsStringAsync().Result;
+ dynamic test = Newtonsoft.Json.JsonConvert.DeserializeObject(returnValue);
+ releaseId = test.id;
+ }
+}
+
+private string ReleaseNotesAsJson()
+{
+ return System.Text.Encodings.Web.JavaScriptEncoder.Default.Encode(System.IO.File.ReadAllText(releaseNotesFile));
+}
+
+private void UploadFileToGitHubRelease(FilePath file)
+{
+ var data = System.IO.File.ReadAllBytes(file.FullPath);
+ var content = new System.Net.Http.ByteArrayContent(data);
+ content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
+
+ using(var client = new System.Net.Http.HttpClient())
+ {
+ client.DefaultRequestHeaders.Authorization =
+ new System.Net.Http.Headers.AuthenticationHeaderValue(
+ "Basic", Convert.ToBase64String(
+ System.Text.ASCIIEncoding.ASCII.GetBytes(
+ $"{gitHubUsername}:{gitHubPassword}")));
+
+ client.DefaultRequestHeaders.Add("User-Agent", "Ocelot Release");
+
+ var result = client.PostAsync($"https://uploads.github.com/repos/ThreeMammals/Ocelot/releases/{releaseId}/assets?name={file.GetFilename()}", content).Result;
+ if(result.StatusCode != System.Net.HttpStatusCode.Created)
+ {
+ throw new Exception("UploadFileToGitHubRelease result.StatusCode = " + result.StatusCode);
+ }
+ }
+}
+
+private void CompleteGitHubRelease()
+{
+ var json = $"{{ \"tag_name\": \"{versioning.NuGetVersion}\", \"target_commitish\": \"main\", \"name\": \"{versioning.NuGetVersion}\", \"body\": \"{ReleaseNotesAsJson()}\", \"draft\": false, \"prerelease\": false }}";
+ var request = new System.Net.Http.HttpRequestMessage(new System.Net.Http.HttpMethod("Patch"), $"https://api.github.com/repos/ThreeMammals/Ocelot/releases/{releaseId}");
+ request.Content = new System.Net.Http.StringContent(json, System.Text.Encoding.UTF8, "application/json");
+
+ using(var client = new System.Net.Http.HttpClient())
+ {
+ client.DefaultRequestHeaders.Authorization =
+ new System.Net.Http.Headers.AuthenticationHeaderValue(
+ "Basic", Convert.ToBase64String(
+ System.Text.ASCIIEncoding.ASCII.GetBytes(
+ $"{gitHubUsername}:{gitHubPassword}")));
+
+ client.DefaultRequestHeaders.Add("User-Agent", "Ocelot Release");
+
+ var result = client.SendAsync(request).Result;
+ if(result.StatusCode != System.Net.HttpStatusCode.OK)
+ {
+ throw new Exception("CompleteGitHubRelease result.StatusCode = " + result.StatusCode);
+ }
+ }
+}
+
+
+/// gets the resource from the specified url
+private string GetResource(string url)
+{
+ try
+ {
+ Information("Getting resource from " + url);
+
+ var assetsRequest = System.Net.WebRequest.CreateHttp(url);
+ assetsRequest.Method = "GET";
+ assetsRequest.Accept = "application/vnd.github.v3+json";
+ assetsRequest.UserAgent = "BuildScript";
+
+ using (var assetsResponse = assetsRequest.GetResponse())
+ {
+ var assetsStream = assetsResponse.GetResponseStream();
+ var assetsReader = new StreamReader(assetsStream);
+ var response = assetsReader.ReadToEnd();
+
+ Information("Response is " + response);
+
+ return response;
+ }
+ }
+ catch(Exception exception)
+ {
+ Information("There was an exception " + exception);
+ throw;
+ }
+}
+
+private bool IsRunningOnCircleCI()
+{
+ return !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("CIRCLECI"));
+}
+
+private bool IsMainOrDevelop()
+{
+ var env = Environment.GetEnvironmentVariable("CIRCLE_BRANCH").ToLower();
+
+ if(env == "main")
+ {
+ return true;
+ }
+
+ if(env == "develop")
+ {
+ return true;
+ }
+
+ return false;
}
\ No newline at end of file
diff --git a/build.ps1 b/build.ps1
deleted file mode 100644
index a336e2985..000000000
--- a/build.ps1
+++ /dev/null
@@ -1,256 +0,0 @@
-##########################################################################
-# This is the Cake bootstrapper script for PowerShell.
-# This file was downloaded from https://github.com/cake-build/resources
-# Feel free to change this file to fit your needs.
-##########################################################################
-
-<#
-
-.SYNOPSIS
-This is a Powershell script to bootstrap a Cake build.
-
-.DESCRIPTION
-This Powershell script will download NuGet if missing, restore NuGet tools (including Cake)
-and execute your Cake build script with the parameters you provide.
-
-.PARAMETER Script
-The build script to execute.
-.PARAMETER Target
-The build script target to run.
-.PARAMETER Configuration
-The build configuration to use.
-.PARAMETER Verbosity
-Specifies the amount of information to be displayed.
-.PARAMETER ShowDescription
-Shows description about tasks.
-.PARAMETER DryRun
-Performs a dry run.
-.PARAMETER SkipToolPackageRestore
-Skips restoring of packages.
-.PARAMETER ScriptArgs
-Remaining arguments are added here.
-
-.LINK
-https://cakebuild.net
-
-#>
-
-[CmdletBinding()]
-Param(
- [string]$Script = "build.cake",
- [string]$Target,
- [string]$Configuration,
- [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")]
- [string]$Verbosity,
- [switch]$ShowDescription,
- [Alias("WhatIf", "Noop")]
- [switch]$DryRun,
- [switch]$SkipToolPackageRestore,
- [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
- [string[]]$ScriptArgs
-)
-
-# Attempt to set highest encryption available for SecurityProtocol.
-# PowerShell will not set this by default (until maybe .NET 4.6.x). This
-# will typically produce a message for PowerShell v2 (just an info
-# message though)
-try {
- # Set TLS 1.2 (3072), then TLS 1.1 (768), then TLS 1.0 (192), finally SSL 3.0 (48)
- # Use integers because the enumeration values for TLS 1.2 and TLS 1.1 won't
- # exist in .NET 4.0, even though they are addressable if .NET 4.5+ is
- # installed (.NET 4.5 is an in-place upgrade).
- # PowerShell Core already has support for TLS 1.2 so we can skip this if running in that.
- if (-not $IsCoreCLR) {
- [System.Net.ServicePointManager]::SecurityProtocol = 3072 -bor 768 -bor 192 -bor 48
- }
- } catch {
- Write-Output 'Unable to set PowerShell to use TLS 1.2 and TLS 1.1 due to old .NET Framework installed. If you see underlying connection closed or trust errors, you may need to upgrade to .NET Framework 4.5+ and PowerShell v3'
- }
-
-[Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null
-function MD5HashFile([string] $filePath)
-{
- if ([string]::IsNullOrEmpty($filePath) -or !(Test-Path $filePath -PathType Leaf))
- {
- return $null
- }
-
- [System.IO.Stream] $file = $null;
- [System.Security.Cryptography.MD5] $md5 = $null;
- try
- {
- $md5 = [System.Security.Cryptography.MD5]::Create()
- $file = [System.IO.File]::OpenRead($filePath)
- return [System.BitConverter]::ToString($md5.ComputeHash($file))
- }
- finally
- {
- if ($file -ne $null)
- {
- $file.Dispose()
- }
- }
-}
-
-function GetProxyEnabledWebClient
-{
- $wc = New-Object System.Net.WebClient
- $proxy = [System.Net.WebRequest]::GetSystemWebProxy()
- $proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials
- $wc.Proxy = $proxy
- return $wc
-}
-
-Write-Host "Preparing to run build script..."
-
-if(!$PSScriptRoot){
- $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
-}
-
-$TOOLS_DIR = Join-Path $PSScriptRoot "tools"
-$ADDINS_DIR = Join-Path $TOOLS_DIR "Addins"
-$MODULES_DIR = Join-Path $TOOLS_DIR "Modules"
-$NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe"
-$CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe"
-$NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe"
-$PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config"
-$PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum"
-$ADDINS_PACKAGES_CONFIG = Join-Path $ADDINS_DIR "packages.config"
-$MODULES_PACKAGES_CONFIG = Join-Path $MODULES_DIR "packages.config"
-
-# Make sure tools folder exists
-if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) {
- Write-Verbose -Message "Creating tools directory..."
- New-Item -Path $TOOLS_DIR -Type Directory | Out-Null
-}
-
-# Make sure that packages.config exist.
-if (!(Test-Path $PACKAGES_CONFIG)) {
- Write-Verbose -Message "Downloading packages.config..."
- try {
- $wc = GetProxyEnabledWebClient
- $wc.DownloadFile("https://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG)
- } catch {
- Throw "Could not download packages.config."
- }
-}
-
-# Try find NuGet.exe in path if not exists
-if (!(Test-Path $NUGET_EXE)) {
- Write-Verbose -Message "Trying to find nuget.exe in PATH..."
- $existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_ -PathType Container) }
- $NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1
- if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) {
- Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)."
- $NUGET_EXE = $NUGET_EXE_IN_PATH.FullName
- }
-}
-
-# Try download NuGet.exe if not exists
-if (!(Test-Path $NUGET_EXE)) {
- Write-Verbose -Message "Downloading NuGet.exe..."
- try {
- $wc = GetProxyEnabledWebClient
- $wc.DownloadFile($NUGET_URL, $NUGET_EXE)
- } catch {
- Throw "Could not download NuGet.exe."
- }
-}
-
-# Save nuget.exe path to environment to be available to child processed
-$env:NUGET_EXE = $NUGET_EXE
-$env:NUGET_EXE_INVOCATION = if ($IsLinux -or $IsMacOS) {
- "mono `"$NUGET_EXE`""
-} else {
- "`"$NUGET_EXE`""
-}
-
-# Restore tools from NuGet?
-if(-Not $SkipToolPackageRestore.IsPresent) {
- Push-Location
- Set-Location $TOOLS_DIR
-
- # Check for changes in packages.config and remove installed tools if true.
- [string] $md5Hash = MD5HashFile $PACKAGES_CONFIG
- if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or
- ($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) {
- Write-Verbose -Message "Missing or changed package.config hash..."
- Get-ChildItem -Exclude packages.config,nuget.exe,Cake.Bakery |
- Remove-Item -Recurse -Force
- }
-
- Write-Verbose -Message "Restoring tools from NuGet..."
-
- $NuGetOutput = Invoke-Expression "& $env:NUGET_EXE_INVOCATION install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`""
-
- if ($LASTEXITCODE -ne 0) {
- Throw "An error occurred while restoring NuGet tools."
- }
- else
- {
- $md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII"
- }
- Write-Verbose -Message ($NuGetOutput | Out-String)
-
- Pop-Location
-}
-
-# Restore addins from NuGet
-if (Test-Path $ADDINS_PACKAGES_CONFIG) {
- Push-Location
- Set-Location $ADDINS_DIR
-
- Write-Verbose -Message "Restoring addins from NuGet..."
- $NuGetOutput = Invoke-Expression "& $env:NUGET_EXE_INVOCATION install -ExcludeVersion -OutputDirectory `"$ADDINS_DIR`""
-
- if ($LASTEXITCODE -ne 0) {
- Throw "An error occurred while restoring NuGet addins."
- }
-
- Write-Verbose -Message ($NuGetOutput | Out-String)
-
- Pop-Location
-}
-
-# Restore modules from NuGet
-if (Test-Path $MODULES_PACKAGES_CONFIG) {
- Push-Location
- Set-Location $MODULES_DIR
-
- Write-Verbose -Message "Restoring modules from NuGet..."
- $NuGetOutput = Invoke-Expression "& $env:NUGET_EXE_INVOCATION install -ExcludeVersion -OutputDirectory `"$MODULES_DIR`""
-
- if ($LASTEXITCODE -ne 0) {
- Throw "An error occurred while restoring NuGet modules."
- }
-
- Write-Verbose -Message ($NuGetOutput | Out-String)
-
- Pop-Location
-}
-
-# Make sure that Cake has been installed.
-if (!(Test-Path $CAKE_EXE)) {
- Throw "Could not find Cake.exe at $CAKE_EXE"
-}
-
-$CAKE_EXE_INVOCATION = if ($IsLinux -or $IsMacOS) {
- "mono `"$CAKE_EXE`""
-} else {
- "`"$CAKE_EXE`""
-}
-
- # Build an array (not a string) of Cake arguments to be joined later
-$cakeArguments = @()
-if ($Script) { $cakeArguments += "`"$Script`"" }
-if ($Target) { $cakeArguments += "-target=`"$Target`"" }
-if ($Configuration) { $cakeArguments += "-configuration=$Configuration" }
-if ($Verbosity) { $cakeArguments += "-verbosity=$Verbosity" }
-if ($ShowDescription) { $cakeArguments += "-showdescription" }
-if ($DryRun) { $cakeArguments += "-dryrun" }
-$cakeArguments += $ScriptArgs
-
-# Start Cake
-Write-Host "Running build script..."
-Invoke-Expression "& $CAKE_EXE_INVOCATION $($cakeArguments -join " ")"
-exit $LASTEXITCODE
diff --git a/build.sh b/build.sh
deleted file mode 100755
index b9e12527f..000000000
--- a/build.sh
+++ /dev/null
@@ -1,117 +0,0 @@
-#!/usr/bin/env bash
-
-##########################################################################
-# This is the Cake bootstrapper script for Linux and OS X.
-# This file was downloaded from https://github.com/cake-build/resources
-# Feel free to change this file to fit your needs.
-##########################################################################
-
-# Define directories.
-SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
-TOOLS_DIR=$SCRIPT_DIR/tools
-ADDINS_DIR=$TOOLS_DIR/Addins
-MODULES_DIR=$TOOLS_DIR/Modules
-NUGET_EXE=$TOOLS_DIR/nuget.exe
-CAKE_EXE=$TOOLS_DIR/Cake/Cake.exe
-PACKAGES_CONFIG=$TOOLS_DIR/packages.config
-PACKAGES_CONFIG_MD5=$TOOLS_DIR/packages.config.md5sum
-ADDINS_PACKAGES_CONFIG=$ADDINS_DIR/packages.config
-MODULES_PACKAGES_CONFIG=$MODULES_DIR/packages.config
-
-# Define md5sum or md5 depending on Linux/OSX
-MD5_EXE=
-if [[ "$(uname -s)" == "Darwin" ]]; then
- MD5_EXE="md5 -r"
-else
- MD5_EXE="md5sum"
-fi
-
-# Define default arguments.
-SCRIPT="build.cake"
-CAKE_ARGUMENTS=()
-
-# Parse arguments.
-for i in "$@"; do
- case $1 in
- -s|--script) SCRIPT="$2"; shift ;;
- --) shift; CAKE_ARGUMENTS+=("$@"); break ;;
- *) CAKE_ARGUMENTS+=("$1") ;;
- esac
- shift
-done
-
-# Make sure the tools folder exist.
-if [ ! -d "$TOOLS_DIR" ]; then
- mkdir "$TOOLS_DIR"
-fi
-
-# Make sure that packages.config exist.
-if [ ! -f "$TOOLS_DIR/packages.config" ]; then
- echo "Downloading packages.config..."
- curl -Lsfo "$TOOLS_DIR/packages.config" https://cakebuild.net/download/bootstrapper/packages
- if [ $? -ne 0 ]; then
- echo "An error occurred while downloading packages.config."
- exit 1
- fi
-fi
-
-# Download NuGet if it does not exist.
-if [ ! -f "$NUGET_EXE" ]; then
- echo "Downloading NuGet..."
- curl -Lsfo "$NUGET_EXE" https://dist.nuget.org/win-x86-commandline/latest/nuget.exe
- if [ $? -ne 0 ]; then
- echo "An error occurred while downloading nuget.exe."
- exit 1
- fi
-fi
-
-# Restore tools from NuGet.
-pushd "$TOOLS_DIR" >/dev/null
-if [ ! -f "$PACKAGES_CONFIG_MD5" ] || [ "$( cat "$PACKAGES_CONFIG_MD5" | sed 's/\r$//' )" != "$( $MD5_EXE "$PACKAGES_CONFIG" | awk '{ print $1 }' )" ]; then
- find . -type d ! -name . ! -name 'Cake.Bakery' | xargs rm -rf
-fi
-
-mono "$NUGET_EXE" install -ExcludeVersion
-if [ $? -ne 0 ]; then
- echo "Could not restore NuGet tools."
- exit 1
-fi
-
-$MD5_EXE "$PACKAGES_CONFIG" | awk '{ print $1 }' >| "$PACKAGES_CONFIG_MD5"
-
-popd >/dev/null
-
-# Restore addins from NuGet.
-if [ -f "$ADDINS_PACKAGES_CONFIG" ]; then
- pushd "$ADDINS_DIR" >/dev/null
-
- mono "$NUGET_EXE" install -ExcludeVersion
- if [ $? -ne 0 ]; then
- echo "Could not restore NuGet addins."
- exit 1
- fi
-
- popd >/dev/null
-fi
-
-# Restore modules from NuGet.
-if [ -f "$MODULES_PACKAGES_CONFIG" ]; then
- pushd "$MODULES_DIR" >/dev/null
-
- mono "$NUGET_EXE" install -ExcludeVersion
- if [ $? -ne 0 ]; then
- echo "Could not restore NuGet modules."
- exit 1
- fi
-
- popd >/dev/null
-fi
-
-# Make sure that Cake has been installed.
-if [ ! -f "$CAKE_EXE" ]; then
- echo "Could not find Cake.exe at '$CAKE_EXE'."
- exit 1
-fi
-
-# Start Cake
-exec mono "$CAKE_EXE" $SCRIPT "${CAKE_ARGUMENTS[@]}"
diff --git a/codeanalysis.ruleset b/codeanalysis.ruleset
index 4b278ba80..0198bd195 100644
--- a/codeanalysis.ruleset
+++ b/codeanalysis.ruleset
@@ -1,5 +1,5 @@
-
+
@@ -50,12 +50,11 @@
-
+
-
@@ -70,14 +69,14 @@
-
+
-
+
diff --git a/docker/Dockerfile.base b/docker/Dockerfile.base
index 02d91f53a..691339490 100644
--- a/docker/Dockerfile.base
+++ b/docker/Dockerfile.base
@@ -1,9 +1,9 @@
-# this is the dockerfile that create the ocelot build container
-# build with the docker-build.sh file in this folder
-FROM mcr.microsoft.com/dotnet/core/sdk:3.1-bionic AS build
-
-RUN apt install gnupg ca-certificates
-RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
-RUN echo "deb https://download.mono-project.com/repo/ubuntu stable-bionic main" | tee /etc/apt/sources.list.d/mono-official-stable.list
-RUN apt update
-RUN apt-get -y install mono-devel
\ No newline at end of file
+FROM mcr.microsoft.com/dotnet/sdk:7.0-alpine
+
+RUN apk add bash icu-libs krb5-libs libgcc libintl libssl1.1 libstdc++ zlib git openssh-client
+
+RUN curl -L --output ./dotnet-install.sh https://dot.net/v1/dotnet-install.sh
+
+RUN chmod u+x ./dotnet-install.sh
+
+RUN ./dotnet-install.sh -c 6.0 -i /usr/share/dotnet
diff --git a/docker/Dockerfile.build b/docker/Dockerfile.build
index 0478da417..5498c6106 100644
--- a/docker/Dockerfile.build
+++ b/docker/Dockerfile.build
@@ -1,15 +1,16 @@
-# call from ocelot repo root with
-# docker build --build-arg OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN -f ./docker/Dockerfile.build .
-FROM mijitt0m/ocelot-build:0.0.1
-
-ARG OCELOT_COVERALLS_TOKEN
-
-ENV OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN
-
-WORKDIR /src
-
-COPY ./. .
-
-RUN chmod u+x build.sh
-
-RUN make build
\ No newline at end of file
+# call from ocelot repo root with
+# docker build --platform linux/arm64 --build-arg OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN -f ./docker/Dockerfile.build .
+# docker build --platform linux/amd64 --build-arg OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN -f ./docker/Dockerfile.build .
+FROM mijitt0m/ocelot-build:0.0.9
+
+ARG OCELOT_COVERALLS_TOKEN
+
+ENV OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN
+
+WORKDIR /build
+
+COPY ./. .
+
+RUN dotnet tool restore
+
+RUN dotnet cake
\ No newline at end of file
diff --git a/docker/Dockerfile.release b/docker/Dockerfile.release
index 5d63816d2..e2659c035 100644
--- a/docker/Dockerfile.release
+++ b/docker/Dockerfile.release
@@ -1,20 +1,20 @@
-# call from ocelot repo root with
-# docker build --build-arg OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN --build-arg OCELOT_GITHUB_API_KEY=$OCELOT_GITHUB_API_KEY --build-arg OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN -f ./docker/Dockerfile.release .
-
-FROM mijitt0m/ocelot-build:0.0.1
-
-ARG OCELOT_COVERALLS_TOKEN
-ARG OCELOT_NUTGET_API_KEY
-ARG OCELOT_GITHUB_API_KEY
-
-ENV OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN
-ENV OCELOT_NUTGET_API_KEY=$OCELOT_NUTGET_API_KEY
-ENV OCELOT_GITHUB_API_KEY=$OCELOT_GITHUB_API_KEY
-
-WORKDIR /src
-
-COPY ./. .
-
-RUN chmod u+x build.sh
-
-RUN make release
\ No newline at end of file
+# call from ocelot repo root with
+# docker build --platform linux/arm64 --build-arg OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN --build-arg OCELOT_GITHUB_API_KEY=$OCELOT_GITHUB_API_KEY --build-arg OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN -f ./docker/Dockerfile.build .
+# docker build --platform linux/amd64 --build-arg OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN --build-arg OCELOT_GITHUB_API_KEY=$OCELOT_GITHUB_API_KEY --build-arg OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN -f ./docker/Dockerfile.build .
+FROM mijitt0m/ocelot-build:0.0.9
+
+ARG OCELOT_COVERALLS_TOKEN
+ARG OCELOT_NUTGET_API_KEY
+ARG OCELOT_GITHUB_API_KEY
+
+ENV OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN
+ENV OCELOT_NUTGET_API_KEY=$OCELOT_NUTGET_API_KEY
+ENV OCELOT_GITHUB_API_KEY=$OCELOT_GITHUB_API_KEY
+
+WORKDIR /build
+
+COPY ./. .
+
+RUN dotnet tool restore
+
+RUN dotnet cake
\ No newline at end of file
diff --git a/docker/build.sh b/docker/build.sh
new file mode 100755
index 000000000..bf2cee9b5
--- /dev/null
+++ b/docker/build.sh
@@ -0,0 +1,7 @@
+# this script build the ocelot docker file
+version=0.0.9
+docker build --platform linux/amd64 -t mijitt0m/ocelot-build -f Dockerfile.base .
+echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin
+docker tag mijitt0m/ocelot-build mijitt0m/ocelot-build:$version
+docker push mijitt0m/ocelot-build:latest
+docker push mijitt0m/ocelot-build:$version
\ No newline at end of file
diff --git a/docker/docker-build.sh b/docker/docker-build.sh
deleted file mode 100755
index 8bb4e2065..000000000
--- a/docker/docker-build.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-# this script build the ocelot docker file
-docker build -t mijitt0m/ocelot-build -f Dockerfile.base .
-echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin
-docker tag mijitt0m/ocelot-build mijitt0m/ocelot-build:0.0.1
-docker push mijitt0m/ocelot-build:latest
-docker push mijitt0m/ocelot-build:0.0.1
diff --git a/docs/building/building.rst b/docs/building/building.rst
index 0a494f9f0..4544eccd9 100644
--- a/docs/building/building.rst
+++ b/docs/building/building.rst
@@ -1,10 +1,10 @@
Building
========
-* The best way to build Ocelot is using the Dockerfile.build file which can be found in the docker folder in Ocelot root. Use the following command `docker build -f ./docker/Dockerfile.build .`.
+* You can also just run `dotnet tool restore && dotnet cake` locally!. Output will got to the `./artifacts` directory.
-* You'll can run the `./build.ps1` or `./build.sh` script depending on your OS. This will compile, run unit and acceptance tests and build the output packages locally. Output will got to the `./artifacts` directory.
+* The best way to replicate the CI process is to build Ocelot locally is using the Dockerfile.build file which can be found in the docker folder in Ocelot root. Use the following command `docker build --platform linux/amd64 -f ./docker/Dockerfile.build .` for example. You will need to change the platform flag depending on your platform.
* There is a Makefile to make it easier to call the various targers in `build.cake`. The scripts are called with .sh but can be easily changed to ps1 if you are using Windows.
-* Alternatively you can build the project in VS2019 with the latest .NET Core SDK.
\ No newline at end of file
+* Alternatively you can build the project in VS2022 with the latest .NET 7.0 SDK.
\ No newline at end of file
diff --git a/docs/building/releaseprocess.rst b/docs/building/releaseprocess.rst
index 43cb5f61a..40da81567 100644
--- a/docs/building/releaseprocess.rst
+++ b/docs/building/releaseprocess.rst
@@ -1,8 +1,8 @@
Release process
===============
-* The release process works best with GitHubFlow branching.
-* Contributors can do whatever they want on PRs and merges to master will result in packages being released to GitHub and NuGet.
+* The release process works best with Git Flow branching.
+* Contributors can do whatever they want on PRs and merges to main will result in packages being released to GitHub and NuGet.
Ocelot uses the following process to accept work into the NuGet packages.
@@ -10,7 +10,7 @@ Ocelot uses the following process to accept work into the NuGet packages.
2. User creates a fork and branches from this (unless a member of core team, they can just create a branch on the main repo) e.g. feat/xxx, fix/xxx etc. It doesn't really matter what the xxx is. It might make sense to use the issue number and maybe a short description. I don't care as long as it has (feat, fix, refactor)/xxx :)
-3. When the user is happy with their work they can create a pull request against master in GitHub with their changes. The user must follow the `SemVer `_ support for this is provided by `GitVersion `_. So if you need to make breaking changes please make sure you use the correct commit message so GitVersion uses the correct semver tags. Do not manually tag the Ocelot repo this will break things.
+3. When the user is happy with their work they can create a pull request against develop in GitHub with their changes. The user must follow the `SemVer `_ support for this is provided by `GitVersion `_. So if you need to make breaking changes please make sure you use the correct commit message so GitVersion uses the correct semver tags. Do not manually tag the Ocelot repo this will break things.
4. The Ocelot team will review the PR and if all is good merge it, else they will suggest feedback that the user will need to act on. In order to speed up getting a PR the user should think about the following.
- Have I covered all my changes with tests at unit and acceptance level?
@@ -23,13 +23,15 @@ In order for a PR to be merged the following must have occured.
- Build must not have slowed down dramatically.
- The main Ocelot package must not have taken on any non MS dependencies.
-5. After the PR is merged to master the release process will begin which builds the code, versions it, pushes artifacts to GitHub and NuGet packages to NuGet.
+5. After the PR is merged to develop the Ocelot NuGet packages will not be updated until a release is created.
-6. The final step is to go back to GitHub and close any issues that are now fixed. You should see something like this in`GitHub `_ and this in `NuGet `_.
+6. When enough work has been completed to justify a new release. Develop will be merged into main the release process will begin which builds the code, versions it, pushes artifacts to GitHub and NuGet packages to NuGet.
+
+7. The final step is to go back to GitHub and close any issues that are now fixed. You should see something like this in`GitHub `_ and this in `NuGet `_.
Notes
-----
All NuGet package builds & releases are done with CircleCI `here _` and all releases are done from `here _`.
-Only TomPallister can merge releases into master at the moment. This is to ensure there is a final quality gate in place. Tom is mainly looking for security issues on the final merge.
+Only TomPallister can merge releases into main at the moment. This is to ensure there is a final quality gate in place. Tom is mainly looking for security issues on the final merge.
diff --git a/docs/building/tests.rst b/docs/building/tests.rst
index d1fc8aba7..c21e91cfd 100644
--- a/docs/building/tests.rst
+++ b/docs/building/tests.rst
@@ -1,24 +1,18 @@
Tests
=====
-The tests should all just run and work apart from the integration tests which need the following
-environmental variables setting. This is a manual step at the moment.
+The tests should all just run and work as part of the build process. You can of course also run them in visual studio.
- ``OCELOT_USERNAME=admin``
- ``OCELOT_HASH=kE/mxd1hO9h9Sl2VhGhwJUd9xZEv4NP6qXoN39nIqM4=``
+Create SSL Cert for Testing
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
- ``OCELOT_SALT=zzWITpnDximUNKYLiUam/w==``
+You can do this via openssl:
-On windows you can use..
-
- ``SETX OCELOT_USERNAME admin``
-
-On mac..
-
- ``export OCELOT_USERNAME=admin``
-
-I need to work out a nicer way of doing this in the future.
+Install openssl package (if you are using Windows, download binaries here).
+Generate private key: `openssl genrsa 2048 > private.pem`
+Generate the self signed certificate: `openssl req -x509 -days 1000 -new -key private.pem -out public.pem`
+If needed, create PFX: `openssl pkcs12 -export -in public.pem -inkey private.pem -out mycert.pfx`
\ No newline at end of file
diff --git a/docs/features/administration.rst b/docs/features/administration.rst
index 4d8919947..6449f583a 100644
--- a/docs/features/administration.rst
+++ b/docs/features/administration.rst
@@ -1,140 +1,131 @@
-Administration
-==============
-
-Ocelot supports changing configuration during runtime via an authenticated HTTP API. This can be authenticated in two ways either using Ocelot's
-internal IdentityServer (for authenticating requests to the administration API only) or hooking the administration API authentication into your own
-IdentityServer.
-
-The first thing you need to do if you want to use the administration API is bring in the relavent NuGet package..
-
-``Install-Package Ocelot.Administration``
-
-This will bring down everything needed by the admin API.
-
-Providing your own IdentityServer
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-All you need to do to hook into your own IdentityServer is add the following to your ConfigureServices method.
-
-.. code-block:: csharp
-
- public virtual void ConfigureServices(IServiceCollection services)
- {
- Action options = o => {
- // o.Authority = ;
- // o.ApiName = ;
- // etc....
- };
-
- services
- .AddOcelot()
- .AddAdministration("/administration", options);
- }
-
-You now need to get a token from your IdentityServer and use in subsequent requests to Ocelot's administration API.
-
-This feature was implemented for `issue 228 `_. It is useful because the IdentityServer authentication
-middleware needs the URL of the IdentityServer. If you are using the internal IdentityServer it might not alaways be possible to have the Ocelot URL.
-
-Internal IdentityServer
-^^^^^^^^^^^^^^^^^^^^^^^
-
-The API is authenticated using bearer tokens that you request from Ocelot iteself. This is provided by the amazing
-`Identity Server `_ project that I have been using for a few years now. Check them out.
-
-In order to enable the administration section you need to do a few things. First of all add this to your
-initial Startup.cs.
-
-The path can be anything you want and it is obviously reccomended don't use
-a url you would like to route through with Ocelot as this will not work. The administration uses the
-MapWhen functionality of asp.net core and all requests to {root}/administration will be sent there not
-to the Ocelot middleware.
-
-The secret is the client secret that Ocelot's internal IdentityServer will use to authenticate requests to the administration API. This can be whatever you want it to be!
-
-.. code-block:: csharp
-
- public virtual void ConfigureServices(IServiceCollection services)
- {
- services
- .AddOcelot()
- .AddAdministration("/administration", "secret");
- }
-
-In order for the administration API to work, Ocelot / IdentityServer must be able to call itself for validation. This means that you need to add the base url of Ocelot
-to global configuration if it is not default (http://localhost:5000). Please note if you are using something like docker to host Ocelot it might not be able to
-call back to localhost etc and you need to know what you are doing with docker networking in this scenario. Anyway this can be done as follows..
-
-If you want to run on a different host and port locally..
-
-.. code-block:: json
-
- "GlobalConfiguration": {
- "BaseUrl": "http://localhost:55580"
- }
-
-or if Ocelot is exposed via dns
-
-.. code-block:: json
-
- "GlobalConfiguration": {
- "BaseUrl": "http://mydns.com"
- }
-
-Now if you went with the configuration options above and want to access the API you can use the postman scripts
-called ocelot.postman_collection.json in the solution to change the Ocelot configuration. Obviously these
-will need to be changed if you are running Ocelot on a different url to http://localhost:5000.
-
-
-The scripts show you how to request a bearer token from ocelot and then use it to GET the existing configuration and POST
-a configuration.
-
-If you are running multiple Ocelot instances in a cluster then you need to use a certificate to sign the bearer tokens used to access the administration API.
-
-In order to do this you need to add two more environmental variables for each Ocelot in the cluster.
-
-``OCELOT_CERTIFICATE``
- The path to a certificate that can be used to sign the tokens. The certificate needs to be of the type X509 and obviously Ocelot needs to be able to access it.
-``OCELOT_CERTIFICATE_PASSWORD``
- The password for the certificate.
-
-Normally Ocelot just uses temporary signing credentials but if you set these environmental variables then it will use the certificate. If all the other Ocelot instances in the cluster have the same certificate then you are good!
-
-
-Administration API
-^^^^^^^^^^^^^^^^^^
-
-**POST {adminPath}/connect/token**
-
-This gets a token for use with the admin area using the client credentials we talk about setting above. Under the hood this calls into an IdentityServer hosted within Ocelot.
-
-The body of the request is form-data as follows
-
-``client_id`` set as admin
-
-``client_secret`` set as whatever you used when setting up the administration services.
-
-``scope`` set as admin
-
-``grant_type`` set as client_credentials
-
-**GET {adminPath}/configuration**
-
-
-This gets the current Ocelot configuration. It is exactly the same JSON we use to set Ocelot up with in the first place.
-
-**POST {adminPath}/configuration**
-
-This overrwrites the existing configuration (should probably be a put!). I reccomend getting your config from the GET endpoint, making any changes and posting it back...simples.
-
-The body of the request is JSON and it is the same format as the FileConfiguration.cs that we use to set up
-Ocelot on a file system.
-
-Please note that if you want to use this API then the process running Ocelot must have permission to write to the disk
-where your ocelot.json or ocelot.{environment}.json is located. This is because Ocelot will overwrite them on save.
-
-**DELETE {adminPath}/outputcache/{region}**
-
-This clears a region of the cache. If you are using a backplane it will clear all instances of the cache! Giving your the ability to run a cluster of Ocelots and cache over all of them in memory and clear them all at the same time / just use a distributed cache.
-
-The region is whatever you set against the Region field in the FileCacheOptions section of the Ocelot configuration.
+Administration
+==============
+
+Ocelot supports changing configuration during runtime via an authenticated HTTP API. This can be authenticated in two ways either using Ocelot's internal IdentityServer (for authenticating requests to the administration API only) or hooking the administration API authentication into your own IdentityServer.
+
+The first thing you need to do if you want to use the administration API is bring in the relavent NuGet package..
+
+``Install-Package Ocelot.Administration``
+
+This will bring down everything needed by the admin API.
+
+Providing your own IdentityServer
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+All you need to do to hook into your own IdentityServer is add the following to your ConfigureServices method.
+
+.. code-block:: csharp
+
+ public virtual void ConfigureServices(IServiceCollection services)
+ {
+ Action options = o =>
+ {
+ o.Authority = identityServerRootUrl;
+ o.RequireHttpsMetadata = false;
+ o.TokenValidationParameters = new TokenValidationParameters
+ {
+ ValidateAudience = false,
+ };
+ // etc....
+ };
+
+ services
+ .AddOcelot()
+ .AddAdministration("/administration", options);
+ }
+
+You now need to get a token from your IdentityServer and use in subsequent requests to Ocelot's administration API.
+
+This feature was implemented for `issue 228 `_. It is useful because the IdentityServer authentication middleware needs the URL of the IdentityServer. If you are using the internal IdentityServer it might not alaways be possible to have the Ocelot URL.
+
+Internal IdentityServer
+^^^^^^^^^^^^^^^^^^^^^^^
+
+The API is authenticated using bearer tokens that you request from Ocelot iteself. This is provided by the amazing `Identity Server `_ project that I have been using for a few years now. Check them out.
+
+In order to enable the administration section you need to do a few things. First of all add this to yourinitial Startup.cs.
+
+The path can be anything you want and it is obviously reccomended don't usea url you would like to route through with Ocelot as this will not work. The administration uses theMapWhen functionality of asp.net core and all requests to {root}/administration will be sent there not to the Ocelot middleware.
+
+The secret is the client secret that Ocelot's internal IdentityServer will use to authenticate requests to the administration API. This can be whatever you want it to be!
+
+.. code-block:: csharp
+
+ public virtual void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddOcelot()
+ .AddAdministration("/administration", "secret");
+ }
+
+In order for the administration API to work, Ocelot / IdentityServer must be able to call itself for validation. This means that you need to add the base url of Ocelot to global configuration if it is not default (http://localhost:5000). Please note if you are using something like docker to host Ocelot it might not be able to call back to localhost etc and you need to know what you are doing with docker networking in this scenario. Anyway this can be done as follows..
+
+If you want to run on a different host and port locally..
+
+.. code-block:: json
+
+ "GlobalConfiguration": {
+ "BaseUrl": "http://localhost:55580"
+ }
+
+or if Ocelot is exposed via dns
+
+.. code-block:: json
+
+ "GlobalConfiguration": {
+ "BaseUrl": "http://mydns.com"
+ }
+
+Now if you went with the configuration options above and want to access the API you can use the postman scriptscalled ocelot.postman_collection.json in the solution to change the Ocelot configuration. Obviously these will need to be changed if you are running Ocelot on a different url to http://localhost:5000.
+
+
+The scripts show you how to request a bearer token from ocelot and then use it to GET the existing configuration and POST a configuration.
+
+If you are running multiple Ocelot instances in a cluster then you need to use a certificate to sign the bearer tokens used to access the administration API.
+
+In order to do this you need to add two more environmental variables for each Ocelot in the cluster.
+
+``OCELOT_CERTIFICATE``
+ The path to a certificate that can be used to sign the tokens. The certificate needs to be of the type X509 and obviously Ocelot needs to be able to access it.
+``OCELOT_CERTIFICATE_PASSWORD``
+ The password for the certificate.
+
+Normally Ocelot just uses temporary signing credentials but if you set these environmental variables then it will use the certificate. If all the other Ocelot instances in thecluster have the same certificate then you are good!
+
+
+Administration API
+^^^^^^^^^^^^^^^^^^
+
+**POST {adminPath}/connect/token**
+
+This gets a token for use with the admin area using the client credentials we talk about setting above. Under the hood this calls into an IdentityServer hosted within Ocelot.
+
+The body of the request is form-data as follows
+
+``client_id`` set as admin
+
+``client_secret`` set as whatever you used when setting up the administration services.
+
+``scope`` set as admin
+
+``grant_type`` set as client_credentials
+
+**GET {adminPath}/configuration**
+
+
+This gets the current Ocelot configuration. It is exactly the same JSON we use to set Ocelot up with in the first place.
+
+**POST {adminPath}/configuration**
+
+This overrwrites the existing configuration (should probably be a put!). I reccomend getting your config from the GET endpoint, making any changes and posting it back...simples.
+
+The body of the request is JSON and it is the same format as the FileConfiguration.cs that we use to set up
+Ocelot on a file system.
+
+Please note that if you want to use this API then the process running Ocelot must have permission to write to the disk where your ocelot.json or ocelot.{environment}.json is located. This is because Ocelot will overwrite them on save.
+
+**DELETE {adminPath}/outputcache/{region}**
+
+This clears a region of the cache. If you are using a backplane it will clear all instances of the cache! Giving your the ability to run a cluster of Ocelots and cache over all of them in memory and clear them all at the same time / just use a distributed cache.
+
+The region is whatever you set against the Region field in the FileCacheOptions section of the Ocelot configuration.
diff --git a/docs/features/authentication.rst b/docs/features/authentication.rst
index 641ca2ed7..e41c0033a 100644
--- a/docs/features/authentication.rst
+++ b/docs/features/authentication.rst
@@ -1,182 +1,177 @@
-Authentication
-==============
-
-In order to authenticate ReRoutes and subsequently use any of Ocelot's claims based features such as authorisation or modifying the request with values from the token. Users must register authentication services in their Startup.cs as usual but they provide a scheme (authentication provider key) with each registration e.g.
-
-.. code-block:: csharp
-
- public void ConfigureServices(IServiceCollection services)
- {
- var authenticationProviderKey = "TestKey";
-
- services.AddAuthentication()
- .AddJwtBearer(authenticationProviderKey, x =>
- {
- });
- }
-
-
-In this example TestKey is the scheme that this provider has been registered with.
-We then map this to a ReRoute in the configuration e.g.
-
-.. code-block:: json
-
- "ReRoutes": [{
- "DownstreamHostAndPorts": [
- {
- "Host": "localhost",
- "Port": 51876,
- }
- ],
- "DownstreamPathTemplate": "/",
- "UpstreamPathTemplate": "/",
- "UpstreamHttpMethod": ["Post"],
- "ReRouteIsCaseSensitive": false,
- "DownstreamScheme": "http",
- "AuthenticationOptions": {
- "AuthenticationProviderKey": "TestKey",
- "AllowedScopes": []
- }
- }]
-
-When Ocelot runs it will look at this ReRoutes AuthenticationOptions.AuthenticationProviderKey
-and check that there is an Authentication provider registered with the given key. If there isn't then Ocelot
-will not start up, if there is then the ReRoute will use that provider when it executes.
-
-If a ReRoute is authenticated Ocelot will invoke whatever scheme is associated with it while executing the authentication middleware. If the request fails authentication Ocelot returns a http status code 401.
-
-JWT Tokens
-^^^^^^^^^^
-
-If you want to authenticate using JWT tokens maybe from a provider like Auth0 you can register your authentication middleware as normal e.g.
-
-.. code-block:: csharp
-
- public void ConfigureServices(IServiceCollection services)
- {
- var authenticationProviderKey = "TestKey";
-
- services.AddAuthentication()
- .AddJwtBearer(authenticationProviderKey, x =>
- {
- x.Authority = "test";
- x.Audience = "test";
- });
-
- services.AddOcelot();
- }
-
-Then map the authentication provider key to a ReRoute in your configuration e.g.
-
-.. code-block:: json
-
- "ReRoutes": [{
- "DownstreamHostAndPorts": [
- {
- "Host": "localhost",
- "Port": 51876,
- }
- ],
- "DownstreamPathTemplate": "/",
- "UpstreamPathTemplate": "/",
- "UpstreamHttpMethod": ["Post"],
- "ReRouteIsCaseSensitive": false,
- "DownstreamScheme": "http",
- "AuthenticationOptions": {
- "AuthenticationProviderKey": "TestKey",
- "AllowedScopes": []
- }
- }]
-
-
-
-Identity Server Bearer Tokens
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-In order to use IdentityServer bearer tokens, register your IdentityServer services as usual in ConfigureServices with a scheme (key). If you don't understand how to do this please consult the IdentityServer documentation.
-
-.. code-block:: csharp
-
- public void ConfigureServices(IServiceCollection services)
- {
- var authenticationProviderKey = "TestKey";
- Action options = o =>
- {
- o.Authority = "https://whereyouridentityserverlives.com";
- o.ApiName = "api";
- o.SupportedTokens = SupportedTokens.Both;
- o.ApiSecret = "secret";
- };
-
- services.AddAuthentication()
- .AddIdentityServerAuthentication(authenticationProviderKey, options);
-
- services.AddOcelot();
- }
-
-Then map the authentication provider key to a ReRoute in your configuration e.g.
-
-.. code-block:: json
-
- "ReRoutes": [{
- "DownstreamHostAndPorts": [
- {
- "Host": "localhost",
- "Port": 51876,
- }
- ],
- "DownstreamPathTemplate": "/",
- "UpstreamPathTemplate": "/",
- "UpstreamHttpMethod": ["Post"],
- "ReRouteIsCaseSensitive": false,
- "DownstreamScheme": "http",
- "AuthenticationOptions": {
- "AuthenticationProviderKey": "TestKey",
- "AllowedScopes": []
- }
- }]
-
-Okta
-^^^^
-Add the following to your startup Configure method:
-
-.. code-block:: csharp
-
- app
- .UseAuthentication()
- .UseOcelot()
- .Wait();
-
-
-Add the following, at minimum, to your startup ConfigureServices method:
-
-.. code-block:: csharp
-
- services
- .AddAuthentication()
- .AddJwtBearer(oktaProviderKey, options =>
- {
- options.Audience = configuration["Authentication:Okta:Audience"]; // Okta Authorization server Audience
- options.Authority = configuration["Authentication:Okta:Server"]; // Okta Authorization Issuer URI URL e.g. https://{subdomain}.okta.com/oauth2/{authidentifier}
- });
- services.AddOcelot(configuration);
-
-
-NOTE: In order to get Ocelot to view the scope claim from Okta properly, you have to add the following to map the default Okta "scp" claim to "scope"
-
-
-.. code-block:: csharp
-
- // Map Okta scp to scope claims instead of http://schemas.microsoft.com/identity/claims/scope to allow ocelot to read/verify them
- JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("scp");
- JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Add("scp", "scope");
-
-
-`Issue 446 `_ that contains some code and examples that might help with Okta integration.
-
-Allowed Scopes
-^^^^^^^^^^^^^
-
-If you add scopes to AllowedScopes Ocelot will get all the user claims (from the token) of the type scope and make sure that the user has all of the scopes in the list.
-
-This is a way to restrict access to a ReRoute on a per scope basis.
+Authentication
+==============
+
+In order to authenticate Routes and subsequently use any of Ocelot's claims based features such as authorization or modifying the request with values from the token. Users must register authentication services in their Startup.cs as usual but they provide a scheme (authentication provider key) with each registration e.g.
+
+.. code-block:: csharp
+
+ public void ConfigureServices(IServiceCollection services)
+ {
+ var authenticationProviderKey = "TestKey";
+
+ services.AddAuthentication()
+ .AddJwtBearer(authenticationProviderKey, x =>
+ {
+ });
+ }
+
+
+In this example TestKey is the scheme that this provider has been registered with. We then map this to a Route in the configuration e.g.
+
+.. code-block:: json
+
+ "Routes": [{
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "localhost",
+ "Port": 51876,
+ }
+ ],
+ "DownstreamPathTemplate": "/",
+ "UpstreamPathTemplate": "/",
+ "UpstreamHttpMethod": ["Post"],
+ "RouteIsCaseSensitive": false,
+ "DownstreamScheme": "http",
+ "AuthenticationOptions": {
+ "AuthenticationProviderKey": "TestKey",
+ "AllowedScopes": []
+ }
+ }]
+
+When Ocelot runs it will look at this Routes AuthenticationOptions.AuthenticationProviderKey and check that there is an Authentication provider registered with the given key. If there isn't then Ocelot will not start up, if there is then the Route will use that provider when it executes.
+
+If a Route is authenticated Ocelot will invoke whatever scheme is associated with it while executing the authentication middleware. If the request fails authentication Ocelot returns a http status code 401.
+
+JWT Tokens
+^^^^^^^^^^
+
+If you want to authenticate using JWT tokens maybe from a provider like Auth0 you can register your authentication middleware as normal e.g.
+
+.. code-block:: csharp
+
+ public void ConfigureServices(IServiceCollection services)
+ {
+ var authenticationProviderKey = "TestKey";
+
+ services.AddAuthentication()
+ .AddJwtBearer(authenticationProviderKey, x =>
+ {
+ x.Authority = "test";
+ x.Audience = "test";
+ });
+
+ services.AddOcelot();
+ }
+
+Then map the authentication provider key to a Route in your configuration e.g.
+
+.. code-block:: json
+
+ "Routes": [{
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "localhost",
+ "Port": 51876,
+ }
+ ],
+ "DownstreamPathTemplate": "/",
+ "UpstreamPathTemplate": "/",
+ "UpstreamHttpMethod": ["Post"],
+ "RouteIsCaseSensitive": false,
+ "DownstreamScheme": "http",
+ "AuthenticationOptions": {
+ "AuthenticationProviderKey": "TestKey",
+ "AllowedScopes": []
+ }
+ }]
+
+
+
+Identity Server Bearer Tokens
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In order to use IdentityServer bearer tokens, register your IdentityServer services as usual in ConfigureServices with a scheme (key). If you don't understand how to do this please consult the IdentityServer documentation.
+
+.. code-block:: csharp
+
+ public void ConfigureServices(IServiceCollection services)
+ {
+ var authenticationProviderKey = "TestKey";
+ Action options = o =>
+ {
+ o.Authority = "https://whereyouridentityserverlives.com";
+ // etc
+ };
+
+ services.AddAuthentication()
+ .AddJwtBearer(authenticationProviderKey, options);
+
+ services.AddOcelot();
+ }
+
+Then map the authentication provider key to a Route in your configuration e.g.
+
+.. code-block:: json
+
+ "Routes": [{
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "localhost",
+ "Port": 51876,
+ }
+ ],
+ "DownstreamPathTemplate": "/",
+ "UpstreamPathTemplate": "/",
+ "UpstreamHttpMethod": ["Post"],
+ "RouteIsCaseSensitive": false,
+ "DownstreamScheme": "http",
+ "AuthenticationOptions": {
+ "AuthenticationProviderKey": "TestKey",
+ "AllowedScopes": []
+ }
+ }]
+
+Okta
+^^^^
+Add the following to your startup Configure method:
+
+.. code-block:: csharp
+
+ app
+ .UseAuthentication()
+ .UseOcelot()
+ .Wait();
+
+
+Add the following, at minimum, to your startup ConfigureServices method:
+
+.. code-block:: csharp
+
+ services
+ .AddAuthentication()
+ .AddJwtBearer(oktaProviderKey, options =>
+ {
+ options.Audience = configuration["Authentication:Okta:Audience"]; // Okta Authorization server Audience
+ options.Authority = configuration["Authentication:Okta:Server"]; // Okta Authorization Issuer URI URL e.g. https://{subdomain}.okta.com/oauth2/{authidentifier}
+ });
+ services.AddOcelot(configuration);
+
+
+NOTE: In order to get Ocelot to view the scope claim from Okta properly, you have to add the following to map the default Okta "scp" claim to "scope"
+
+
+.. code-block:: csharp
+
+ // Map Okta scp to scope claims instead of http://schemas.microsoft.com/identity/claims/scope to allow ocelot to read/verify them
+ JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("scp");
+ JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Add("scp", "scope");
+
+
+`Issue 446 `_ that contains some code and examples that might help with Okta integration.
+
+Allowed Scopes
+^^^^^^^^^^^^^
+
+If you add scopes to AllowedScopes Ocelot will get all the user claims (from the token) of the type scope and make sure that the user has all of the scopes in the list.
+
+This is a way to restrict access to a Route on a per scope basis.
diff --git a/docs/features/authorisation.rst b/docs/features/authorisation.rst
deleted file mode 100644
index b7a4ec1ee..000000000
--- a/docs/features/authorisation.rst
+++ /dev/null
@@ -1,18 +0,0 @@
-Authorisation
-=============
-
-Ocelot supports claims based authorisation which is run post authentication. This means if
-you have a route you want to authorise you can add the following to you ReRoute configuration.
-
-.. code-block:: json
-
- "RouteClaimsRequirement": {
- "UserType": "registered"
- }
-
-In this example when the authorisation middleware is called Ocelot will check to see
-if the user has the claim type UserType and if the value of that claim is registered.
-If it isn't then the user will not be authorised and the response will be 403 forbidden.
-
-
-
diff --git a/docs/features/authorization.rst b/docs/features/authorization.rst
new file mode 100644
index 000000000..c890c14c0
--- /dev/null
+++ b/docs/features/authorization.rst
@@ -0,0 +1,15 @@
+Authorization
+=============
+
+Ocelot supports claims based authorization which is run post authentication. This means if you have a route you want to authorize you can add the following to you Route configuration.
+
+.. code-block:: json
+
+ "RouteClaimsRequirement": {
+ "UserType": "registered"
+ }
+
+In this example when the authorization middleware is called Ocelot will check to seeif the user has the claim type UserType and if the value of that claim is registered. If it isn't then the user will not be authorized and the response will be 403 forbidden.
+
+
+
diff --git a/docs/features/caching.rst b/docs/features/caching.rst
index c4be74f42..f6c2a7fd2 100644
--- a/docs/features/caching.rst
+++ b/docs/features/caching.rst
@@ -1,10 +1,7 @@
Caching
=======
-Ocelot supports some very rudimentary caching at the moment provider by
-the `CacheManager `_ project. This is an amazing project
-that is solving a lot of caching problems. I would reccomend using this package to
-cache with Ocelot.
+Ocelot supports some very rudimentary caching at the moment provider by the `CacheManager `_ project. This is an amazing project that is solving a lot of caching problems. I would reccomend using this package to cache with Ocelot.
The following example shows how to add CacheManager to Ocelot so that you can do output caching.
@@ -24,7 +21,7 @@ The second thing you need to do something like the following to your ConfigureSe
x.WithDictionaryHandle();
})
-Finally in order to use caching on a route in your ReRoute configuration add this setting.
+Finally in order to use caching on a route in your Route configuration add this setting.
.. code-block:: json
@@ -32,19 +29,14 @@ Finally in order to use caching on a route in your ReRoute configuration add thi
In this example ttl seconds is set to 15 which means the cache will expire after 15 seconds.
-If you look at the example `here `_ you can see how the cache manager
-is setup and then passed into the Ocelot AddCacheManager configuration method. You can use any settings supported by
-the CacheManager package and just pass them in.
+If you look at the example `here `_ you can see how the cache manager is setup and then passed into the Ocelot AddCacheManager configuration method. You can use any settings supported by the CacheManager package and just pass them in.
-Anyway Ocelot currently supports caching on the URL of the downstream service
-and setting a TTL in seconds to expire the cache. You can also clear the cache for a region
-by calling Ocelot's administration API.
+Anyway Ocelot currently supports caching on the URL of the downstream service and setting a TTL in seconds to expire the cache. You can also clear the cache for a region by calling Ocelot's administration API.
Your own caching
^^^^^^^^^^^^^^^^
-If you want to add your own caching method implement the following interfaces and register them in DI
-e.g. ``services.AddSingleton, MyCache>()``
+If you want to add your own caching method implement the following interfaces and register them in DI e.g. ``services.AddSingleton, MyCache>()``
``IOcelotCache`` this is for output caching.
diff --git a/docs/features/claimstransformation.rst b/docs/features/claimstransformation.rst
index 8d4013176..c82657a4c 100644
--- a/docs/features/claimstransformation.rst
+++ b/docs/features/claimstransformation.rst
@@ -1,35 +1,17 @@
Claims Transformation
=====================
-Ocelot allows the user to access claims and transform them into headers, query string
-parameters, other claims and change downstream paths. This is only available once a user
-has been authenticated.
+Ocelot allows the user to access claims and transform them into headers, query string parameters, other claims and change downstream paths. This is only available once a user has been authenticated.
-After the user is authenticated we run the claims to claims transformation middleware.
-This allows the user to transform claims before the authorisation middleware is called.
-After the user is authorised first we call the claims to headers middleware, then
-the claims to query string parameters middleware, and Finally the claims to downstream path
-middleware.
+After the user is authenticated we run the claims to claims transformation middleware. This allows the user to transform claims before the authorization middleware is called. After the user is authorized first we call the claims to headers middleware, thenthe claims to query string parameters middleware, and Finally the claims to downstream pathmiddleware.
-The syntax for performing the transforms is the same for each process. In the ReRoute
-configuration a json dictionary is added with a specific name either AddClaimsToRequest,
-AddHeadersToRequest, AddQueriesToRequest, or ChangeDownstreamPathTemplate.
+The syntax for performing the transforms is the same for each process. In the Route configuration a json dictionary is added with a specific name either AddClaimsToRequest, AddHeadersToRequest, AddQueriesToRequest, or ChangeDownstreamPathTemplate.
Note: I'm not a hotshot programmer so have no idea if this syntax is good...
-Within this dictionary the entries specify how Ocelot should transform things!
-The key to the dictionary is going to become the key of either a claim, header
-or query parameter. In the case of ChangeDownstreamPathTemplate, the key must be
-also specified in the DownstreamPathTemplate, in order to do the transformation.
+Within this dictionary the entries specify how Ocelot should transform things! The key to the dictionary is going to become the key of either a claim, header or query parameter. In the case of ChangeDownstreamPathTemplate, the key must be also specified in the DownstreamPathTemplate, in order to do the transformation.
-The value of the entry is parsed to logic that will perform the transform. First of
-all a dictionary accessor is specified e.g. Claims[CustomerId]. This means we want
-to access the claims and get the CustomerId claim type. Next is a greater than (>)
-symbol which is just used to split the string. The next entry is either value or value with
-and indexer. If value is specified Ocelot will just take the value and add it to the
-transform. If the value has an indexer Ocelot will look for a delimiter which is provided
-after another greater than symbol. Ocelot will then split the value on the delimiter
-and add whatever was at the index requested to the transform.
+The value of the entry is parsed to logic that will perform the transform. First ofall a dictionary accessor is specified e.g. Claims[CustomerId]. This means we want to access the claims and get the CustomerId claim type. Next is a greater than (>)symbol which is just used to split the string. The next entry is either value or value with an indexer. If value is specified Ocelot will just take the value and add it to the transform. If the value has an indexer Ocelot will look for a delimiter which is provided after another greater than symbol. Ocelot will then split the value on the delimiter and add whatever was at the index requested to the transform.
Claims to Claims Transformation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -43,8 +25,7 @@ Below is an example configuration that will transforms claims to claims
"UserId": "Claims[sub] > value[1] > |"
}
-This shows a transforms where Ocelot looks at the users sub claim and transforms it into
-UserType and UserId claims. Assuming the sub looks like this "usertypevalue|useridvalue".
+This shows a transforms where Ocelot looks at the users sub claim and transforms it into UserType and UserId claims. Assuming the sub looks like this "usertypevalue|useridvalue".
Claims to Headers Tranformation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -57,8 +38,7 @@ Below is an example configuration that will transforms claims to headers
"CustomerId": "Claims[sub] > value[1] > |"
}
-This shows a transform where Ocelot looks at the users sub claim and transforms it into a
-CustomerId header. Assuming the sub looks like this "usertypevalue|useridvalue".
+This shows a transform where Ocelot looks at the users sub claim and transforms it into a CustomerId header. Assuming the sub looks like this "usertypevalue|useridvalue".
Claims to Query String Parameters Transformation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -71,8 +51,7 @@ Below is an example configuration that will transforms claims to query string pa
"LocationId": "Claims[LocationId] > value",
}
-This shows a transform where Ocelot looks at the users LocationId claim and add it as
-a query string parameter to be forwarded onto the downstream service.
+This shows a transform where Ocelot looks at the users LocationId claim and add it as a query string parameter to be forwarded onto the downstream service.
Claims to Downstream Path Transformation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -87,10 +66,7 @@ Below is an example configuration that will transform claims to downstream path
"userId": "Claims[sub] > value[1] > |",
}
-This shows a transform where Ocelot looks at the users userId claim and substitutes the value
-to the "{userId}" placeholder specified in the DownstreamPathTemplate. Take into account that the
-key specified in the ChangeDownstreamPathTemplate must be the same than the placeholder specified in
+This shows a transform where Ocelot looks at the users userId claim and substitutes the value to the "{userId}" placeholder specified in the DownstreamPathTemplate. Take into account that the key specified in the ChangeDownstreamPathTemplate must be the same than the placeholder specified in
the DownstreamPathTemplate.
-Note: if a key specified in the ChangeDownstreamPathTemplate does not exist as a placeholder in DownstreamPathTemplate
-it will fail at runtime returning an error in the response.
\ No newline at end of file
+Note: if a key specified in the ChangeDownstreamPathTemplate does not exist as a placeholder in DownstreamPathTemplate it will fail at runtime returning an error in the response.
\ No newline at end of file
diff --git a/docs/features/configuration.rst b/docs/features/configuration.rst
index 7983af397..d41b05a88 100644
--- a/docs/features/configuration.rst
+++ b/docs/features/configuration.rst
@@ -1,20 +1,16 @@
Configuration
============
-An example configuration can be found `here `_.
-There are two sections to the configuration. An array of ReRoutes and a GlobalConfiguration.
-The ReRoutes are the objects that tell Ocelot how to treat an upstream request. The Global
-configuration is a bit hacky and allows overrides of ReRoute specific settings. It's useful
-if you don't want to manage lots of ReRoute specific settings.
+An example configuration can be found `here `_. There are two sections to the configuration. An array of Routes and a GlobalConfiguration. The Routes are the objects that tell Ocelot how to treat an upstream request. The Global configuration is a bit hacky and allows overrides of Route specific settings. It's useful if you don't want to manage lots of Route specific settings.
.. code-block:: json
{
- "ReRoutes": [],
+ "Routes": [],
"GlobalConfiguration": {}
}
-Here is an example ReRoute configuration, You don't need to set all of these things but this is everything that is available at the moment:
+Here is an example Route configuration, You don't need to set all of these things but this is everything that is available at the moment:
.. code-block:: json
@@ -24,6 +20,8 @@ Here is an example ReRoute configuration, You don't need to set all of these thi
"UpstreamHttpMethod": [
"Get"
],
+ "DownstreamHttpMethod": "",
+ "DownstreamHttpVersion": "",
"AddHeadersToRequest": {},
"AddClaimsToRequest": {},
"RouteClaimsRequirement": {},
@@ -33,7 +31,7 @@ Here is an example ReRoute configuration, You don't need to set all of these thi
"TtlSeconds": 0,
"Region": ""
},
- "ReRouteIsCaseSensitive": false,
+ "RouteIsCaseSensitive": false,
"ServiceName": "",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
@@ -114,7 +112,7 @@ Instead of adding the configuration directly e.g. AddJsonFile("ocelot.json") you
In this scenario Ocelot will look for any files that match the pattern (?i)ocelot.([a-zA-Z0-9]*).json and then merge these together. If you want to set the GlobalConfiguration property you must have a file called ocelot.global.json.
-The way Ocelot merges the files is basically load them, loop over them, add any ReRoutes, add any AggregateReRoutes and if the file is called ocelot.global.json add the GlobalConfiguration aswell as any ReRoutes or AggregateReRoutes. Ocelot will then save the merged configuration to a file called ocelot.json and this will be used as the source of truth while ocelot is running.
+The way Ocelot merges the files is basically load them, loop over them, add any Routes, add any AggregateRoutes and if the file is called ocelot.global.json add the GlobalConfiguration aswell as any Routes or AggregateRoutes. Ocelot will then save the merged configuration to a file called ocelot.json and this will be used as the source of truth while ocelot is running.
At the moment there is no validation at this stage it only happens when Ocelot validates the final merged configuration. This is something to be aware of when you are investigating problems. I would advise always checking what is in ocelot.json if you have any problems.
@@ -150,8 +148,7 @@ Then you add the following when you register your services Ocelot will attempt t
.AddConsul()
.AddConfigStoredInConsul();
-You also need to add the following to your ocelot.json. This is how Ocelot
-finds your Consul agent and interacts to load and store the configuration from Consul.
+You also need to add the following to your ocelot.json. This is how Ocelot finds your Consul agent and interacts to load and store the configuration from Consul.
.. code-block:: json
@@ -162,8 +159,7 @@ finds your Consul agent and interacts to load and store the configuration from C
}
}
-I decided to create this feature after working on the Raft consensus algorithm and finding out its super hard. Why not take advantage of the fact Consul already gives you this!
-I guess it means if you want to use Ocelot to its fullest you take on Consul as a dependency for now.
+I decided to create this feature after working on the Raft consensus algorithm and finding out its super hard. Why not take advantage of the fact Consul already gives you this! I guess it means if you want to use Ocelot to its fullest you take on Consul as a dependency for now.
This feature has a 3 second ttl cache before making a new request to your local consul agent.
@@ -199,24 +195,16 @@ If you do not set the ConfigurationKey Ocelot will use the string InternalConfig
Follow Redirects / Use CookieContainer
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-Use HttpHandlerOptions in ReRoute configuration to set up HttpHandler behavior:
+Use HttpHandlerOptions in Route configuration to set up HttpHandler behavior:
-1. AllowAutoRedirect is a value that indicates whether the request should follow redirection responses. Set it true if the request should automatically
-follow redirection responses from the Downstream resource; otherwise false. The default value is false.
+1. AllowAutoRedirect is a value that indicates whether the request should follow redirection responses. Set it true if the request should automatically follow redirection responses from the Downstream resource; otherwise false. The default value is false.
-2. UseCookieContainer is a value that indicates whether the handler uses the CookieContainer
-property to store server cookies and uses these cookies when sending requests. The default value is false. Please note
-that if you are using the CookieContainer Ocelot caches the HttpClient for each downstream service. This means that all requests
-to that DownstreamService will share the same cookies. `Issue 274 `_ was created because a user
-noticed that the cookies were being shared. I tried to think of a nice way to handle this but I think it is impossible. If you don't cache the clients
-that means each request gets a new client and therefore a new cookie container. If you clear the cookies from the cached client container you get race conditions due to inflight
-requests. This would also mean that subsequent requests don't use the cookies from the previous response! All in all not a great situation. I would avoid setting
-UseCookieContainer to true unless you have a really really good reason. Just look at your response headers and forward the cookies back with your next request!
+2. UseCookieContainer is a value that indicates whether the handler uses the CookieContainer property to store server cookies and uses these cookies when sending requests. The default value is false. Please note that if you are using the CookieContainer Ocelot caches the HttpClient for each downstream service. This means that all requests to that DownstreamService will share the same cookies. `Issue 274 `_ was created because a user noticed that the cookies were being shared. I tried to think of a nice way to handle this but I think it is impossible. If you don't cache the clients that means each request gets a new client and therefore a new cookie container. If you clear the cookies from the cached client container you get race conditions due to inflight requests. This would also mean that subsequent requests don't use the cookies from the previous response! All in all not a great situation. I would avoid setting UseCookieContainer to true unless you have a really really good reason. Just look at your response headers and forward the cookies back with your next request!
SSL Errors
^^^^^^^^^^
-If you want to ignore SSL warnings / errors set the following in your ReRoute config.
+If you want to ignore SSL warnings / errors set the following in your Route config.
.. code-block:: json
@@ -227,4 +215,59 @@ I don't recommend doing this, I suggest creating your own certificate and then g
MaxConnectionsPerServer
^^^^^^^^^^^^^^^^^^^^^^^
-This controls how many connections the internal HttpClient will open. This can be set at ReRoute or global level.
\ No newline at end of file
+This controls how many connections the internal HttpClient will open. This can be set at Route or global level.
+
+React to Configuration Changes
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Resolve IOcelotConfigurationChangeTokenSource from the DI container if you wish to react to changes to the Ocelot configuration via the Ocelot.Administration API or ocelot.json being reloaded from the disk. You may either poll the change token's HasChanged property, or register a callback with the RegisterChangeCallback method.
+
+Polling the HasChanged property
+-------------------------------
+
+.. code-block:: csharp
+ public class ConfigurationNotifyingService : BackgroundService
+ {
+ private readonly IOcelotConfigurationChangeTokenSource _tokenSource;
+ private readonly ILogger _logger;
+ public ConfigurationNotifyingService(IOcelotConfigurationChangeTokenSource tokenSource, ILogger logger)
+ {
+ _tokenSource = tokenSource;
+ _logger = logger;
+ }
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ while (!stoppingToken.IsCancellationRequested)
+ {
+ if (_tokenSource.ChangeToken.HasChanged)
+ {
+ _logger.LogInformation("Configuration updated");
+ }
+ await Task.Delay(1000, stoppingToken);
+ }
+ }
+ }
+
+Registering a callback
+----------------------
+
+.. code-block:: csharp
+ public class MyDependencyInjectedClass : IDisposable
+ {
+ private readonly IOcelotConfigurationChangeTokenSource _tokenSource;
+ private readonly IDisposable _callbackHolder;
+ public MyClass(IOcelotConfigurationChangeTokenSource tokenSource)
+ {
+ _tokenSource = tokenSource;
+ _callbackHolder = tokenSource.ChangeToken.RegisterChangeCallback(_ => Console.WriteLine("Configuration changed"), null);
+ }
+ public void Dispose()
+ {
+ _callbackHolder.Dispose();
+ }
+ }
+
+DownstreamHttpVersion
+---------------------
+
+Ocelot allows you to choose the HTTP version it will use to make the proxy request. It can be set as "1.0", "1.1" or "2.0".
\ No newline at end of file
diff --git a/docs/features/delegatinghandlers.rst b/docs/features/delegatinghandlers.rst
index 1da13c5f6..aaf325b78 100644
--- a/docs/features/delegatinghandlers.rst
+++ b/docs/features/delegatinghandlers.rst
@@ -1,66 +1,64 @@
-Delegating Handlers
-===================
-
-Ocelot allows the user to add delegating handlers to the HttpClient transport. This feature was requested `GitHub #208 `_
-and I decided that it was going to be useful in various ways. Since then we extended it in `GitHub #264 `_.
-
-Usage
-^^^^^
-
-In order to add delegating handlers to the HttpClient transport you need to do two main things.
-
-First in order to create a class that can be used a delegating handler it must look as follows. We are going to register these handlers in the
-asp.net core container so you can inject any other services you have registered into the constructor of your handler.
-
-.. code-block:: csharp
-
- public class FakeHandler : DelegatingHandler
- {
- protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
- {
- //do stuff and optionally call the base handler..
- return await base.SendAsync(request, cancellationToken);
- }
- }
-
-Next you must add the handlers to Ocelot's container like below...
-
-.. code-block:: csharp
-
- services.AddOcelot()
- .AddDelegatingHandler()
- .AddDelegatingHandler()
-
-Both of these Add methods have a default parameter called global which is set to false. If it is false then the intent of
-the DelegatingHandler is to be applied to specific ReRoutes via ocelot.json (more on that later). If it is set to true
-then it becomes a global handler and will be applied to all ReRoutes.
-
-e.g.
-
-As below...
-
-.. code-block:: csharp
-
- services.AddOcelot()
- .AddDelegatingHandler(true)
-
-Finally if you want ReRoute specific DelegatingHandlers or to order your specific and / or global (more on this later) DelegatingHandlers
-then you must add the following json to the specific ReRoute in ocelot.json. The names in the array must match the class names of your
-DelegatingHandlers for Ocelot to match them together.
-
-.. code-block:: json
-
- "DelegatingHandlers": [
- "FakeHandlerTwo",
- "FakeHandler"
- ]
-
-You can have as many DelegatingHandlers as you want and they are run in the following order:
-
-1. Any globals that are left in the order they were added to services and are not in the DelegatingHandlers array from ocelot.json.
-2. Any non global DelegatingHandlers plus any globals that were in the DelegatingHandlers array from ocelot.json ordered as they are in the DelegatingHandlers array.
-3. Tracing DelegatingHandler if enabled (see tracing docs).
-4. QoS DelegatingHandler if enabled (see QoS docs).
-5. The HttpClient sends the HttpRequestMessage.
-
-Hopefully other people will find this feature useful!
+Delegating Handlers
+===================
+
+Ocelot allows the user to add delegating handlers to the HttpClient transport. This feature was requested `GitHub #208 `_
+and I decided that it was going to be useful in various ways. Since then we extended it in `GitHub #264 `_.
+
+Usage
+^^^^^
+
+In order to add delegating handlers to the HttpClient transport you need to do two main things.
+
+First in order to create a class that can be used a delegating handler it must look as follows. We are going to register these handlers in the
+asp.net core container so you can inject any other services you have registered into the constructor of your handler.
+
+.. code-block:: csharp
+
+ public class FakeHandler : DelegatingHandler
+ {
+ protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
+ {
+ //do stuff and optionally call the base handler..
+ return await base.SendAsync(request, cancellationToken);
+ }
+ }
+
+Next you must add the handlers to Ocelot's container like below...
+
+.. code-block:: csharp
+
+ services.AddOcelot()
+ .AddDelegatingHandler()
+ .AddDelegatingHandler()
+
+Both of these Add methods have a default parameter called global which is set to false. If it is false then the intent of the DelegatingHandler is to be applied to specific Routes via ocelot.json (more on that later). If it is set to true
+then it becomes a global handler and will be applied to all Routes.
+
+e.g.
+
+As below...
+
+.. code-block:: csharp
+
+ services.AddOcelot()
+ .AddDelegatingHandler(true)
+
+Finally if you want Route specific DelegatingHandlers or to order your specific and / or global (more on this later) DelegatingHandlers then you must add the following json to the specific Route in ocelot.json. The names in the array must match the class names of your
+DelegatingHandlers for Ocelot to match them together.
+
+.. code-block:: json
+
+ "DelegatingHandlers": [
+ "FakeHandlerTwo",
+ "FakeHandler"
+ ]
+
+You can have as many DelegatingHandlers as you want and they are run in the following order:
+
+1. Any globals that are left in the order they were added to services and are not in the DelegatingHandlers array from ocelot.json.
+2. Any non global DelegatingHandlers plus any globals that were in the DelegatingHandlers array from ocelot.json ordered as they are in the DelegatingHandlers array.
+3. Tracing DelegatingHandler if enabled (see tracing docs).
+4. QoS DelegatingHandler if enabled (see QoS docs).
+5. The HttpClient sends the HttpRequestMessage.
+
+Hopefully other people will find this feature useful!
diff --git a/docs/features/errorcodes.rst b/docs/features/errorcodes.rst
new file mode 100644
index 000000000..d07eb3a49
--- /dev/null
+++ b/docs/features/errorcodes.rst
@@ -0,0 +1,13 @@
+Http Error Status Codes
+=======================
+
+Ocelot will return HTTP status error codes based on internal logic in certain siturations:
+- 401 if the authentication middleware runs and the user is not authenticated.
+- 403 if the authorization middleware runs and the user is unauthenticated, claim value not authroised, scope not authorized, user doesnt have required claim or cannot find claim.
+- 503 if the downstream request times out.
+- 499 if the request is cancelled by the client.
+- 404 if unable to find a downstream route.
+- 502 if unable to connect to downstream service.
+- 500 if unable to complete the HTTP request downstream and the exception is not OperationCanceledException or HttpRequestException.
+- 404 if Ocelot is unable to map an internal error code to a HTTP status code.
+
diff --git a/docs/features/graphql.rst b/docs/features/graphql.rst
index 36006fae1..5c74c0564 100644
--- a/docs/features/graphql.rst
+++ b/docs/features/graphql.rst
@@ -1,14 +1,11 @@
GraphQL
=======
-OK you got me Ocelot doesn't directly support GraphQL but so many people have asked about it I wanted to show how easy it is to integrate
-the `graphql-dotnet `_ library.
+OK you got me Ocelot doesn't directly support GraphQL but so many people have asked about it I wanted to show how easy it is to integrate the `graphql-dotnet `_ library.
-Please see the sample project `OcelotGraphQL `_.
-Using a combination of the graphql-dotnet project and Ocelot's DelegatingHandler features this is pretty easy to do.
-However I do not intend to integrate more closely with GraphQL at the moment. Check out the samples readme and that should give
-you enough instruction on how to do this!
+Please see the sample project `OcelotGraphQL `_. Using a combination of the graphql-dotnet project and Ocelot's DelegatingHandler features this is pretty easy to do.
+However I do not intend to integrate more closely with GraphQL at the moment. Check out the samples readme and that should give you enough instruction on how to do this!
Good luck and have fun :>
diff --git a/docs/features/headerstransformation.rst b/docs/features/headerstransformation.rst
index ed772d612..a7f9d50a0 100644
--- a/docs/features/headerstransformation.rst
+++ b/docs/features/headerstransformation.rst
@@ -1,150 +1,150 @@
-Headers Transformation
-======================
-
-Ocelot allows the user to transform headers pre and post downstream request. At the moment Ocelot only supports find and replace. This feature was requested `GitHub #190 `_ and I decided that it was going to be useful in various ways.
-
-Add to Request
-^^^^^^^^^^^^^^
-
-This feature was requestes in `GitHub #313 `_.
-
-If you want to add a header to your upstream request please add the following to a ReRoute in your ocelot.json:
-
-.. code-block:: json
-
- "UpstreamHeaderTransform": {
- "Uncle": "Bob"
- }
-
-In the example above a header with the key Uncle and value Bob would be send to to the upstream service.
-
-Placeholders are supported too (see below).
-
-Add to Response
-^^^^^^^^^^^^^^^
-
-This feature was requested in `GitHub #280 `_.
-
-If you want to add a header to your downstream response please add the following to a ReRoute in ocelot.json..
-
-.. code-block:: json
-
- "DownstreamHeaderTransform": {
- "Uncle": "Bob"
- },
-
-In the example above a header with the key Uncle and value Bob would be returned by Ocelot when requesting the specific ReRoute.
-
-If you want to return the Butterfly APM trace id then do something like the following..
-
-.. code-block:: json
-
- "DownstreamHeaderTransform": {
- "AnyKey": "{TraceId}"
- },
-
-Find and Replace
-^^^^^^^^^^^^^^^^
-
-In order to transform a header first we specify the header key and then the type of transform we want e.g.
-
-.. code-block:: json
-
- "Test": "http://www.bbc.co.uk/, http://ocelot.com/"
-
-The key is "Test" and the value is "http://www.bbc.co.uk/, http://ocelot.com/". The value is saying replace http://www.bbc.co.uk/ with http://ocelot.com/. The syntax is {find}, {replace}. Hopefully pretty simple. There are examples below that explain more.
-
-Pre Downstream Request
-^^^^^^^^^^^^^^^^^^^^^^
-
-Add the following to a ReRoute in ocelot.json in order to replace http://www.bbc.co.uk/ with http://ocelot.com/. This header will be changed before the request downstream and will be sent to the downstream server.
-
-.. code-block:: json
-
- "UpstreamHeaderTransform": {
- "Test": "http://www.bbc.co.uk/, http://ocelot.com/"
- },
-
-Post Downstream Request
-^^^^^^^^^^^^^^^^^^^^^^^
-
-Add the following to a ReRoute in ocelot.json in order to replace http://www.bbc.co.uk/ with http://ocelot.com/. This transformation will take place after Ocelot has received the response from the downstream service.
-
-.. code-block:: json
-
- "DownstreamHeaderTransform": {
- "Test": "http://www.bbc.co.uk/, http://ocelot.com/"
- },
-
-Placeholders
-^^^^^^^^^^^^
-
-Ocelot allows placeholders that can be used in header transformation.
-
-{RemoteIpAddress} - This will find the clients IP address using _httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString() so you will get back some IP.
-{BaseUrl} - This will use Ocelot's base url e.g. http://localhost:5000 as its value.
-{DownstreamBaseUrl} - This will use the downstream services base url e.g. http://localhost:5000 as its value. This only works for DownstreamHeaderTransform at the moment.
-{TraceId} - This will use the Butterfly APM Trace Id. This only works for DownstreamHeaderTransform at the moment.
-
-Handling 302 Redirects
-^^^^^^^^^^^^^^^^^^^^^^
-Ocelot will by default automatically follow redirects however if you want to return the location header to the client you might want to change the location to be Ocelot not the downstream service. Ocelot allows this with the following configuration.
-
-.. code-block:: json
-
- "DownstreamHeaderTransform": {
- "Location": "http://www.bbc.co.uk/, http://ocelot.com/"
- },
- "HttpHandlerOptions": {
- "AllowAutoRedirect": false,
- },
-
-or you could use the BaseUrl placeholder.
-
-.. code-block:: json
-
- "DownstreamHeaderTransform": {
- "Location": "http://localhost:6773, {BaseUrl}"
- },
- "HttpHandlerOptions": {
- "AllowAutoRedirect": false,
- },
-
-finally if you are using a load balancer with Ocelot you will get multiple downstream base urls so the above would not work. In this case you can do the following.
-
-.. code-block:: json
-
- "DownstreamHeaderTransform": {
- "Location": "{DownstreamBaseUrl}, {BaseUrl}"
- },
- "HttpHandlerOptions": {
- "AllowAutoRedirect": false,
- },
-
-X-Forwarded-For
-^^^^^^^^^^^^^^^
-
-An example of using {RemoteIpAddress} placeholder...
-
-.. code-block:: json
-
- "UpstreamHeaderTransform": {
- "X-Forwarded-For": "{RemoteIpAddress}"
- }
-
-Future
-^^^^^^
-
-Ideally this feature would be able to support the fact that a header can have multiple values. At the moment it just assumes one.
-It would also be nice if it could multi find and replace e.g.
-
-.. code-block:: json
-
- "DownstreamHeaderTransform": {
- "Location": "[{one,one},{two,two}"
- },
- "HttpHandlerOptions": {
- "AllowAutoRedirect": false,
- },
-
-If anyone wants to have a go at this please help yourself!!
+Headers Transformation
+======================
+
+Ocelot allows the user to transform headers pre and post downstream request. At the moment Ocelot only supports find and replace. This feature was requested `GitHub #190 `_ and I decided that it was going to be useful in various ways.
+
+Add to Request
+^^^^^^^^^^^^^^
+
+This feature was requestes in `GitHub #313 `_.
+
+If you want to add a header to your upstream request please add the following to a Route in your ocelot.json:
+
+.. code-block:: json
+
+ "UpstreamHeaderTransform": {
+ "Uncle": "Bob"
+ }
+
+In the example above a header with the key Uncle and value Bob would be send to to the upstream service.
+
+Placeholders are supported too (see below).
+
+Add to Response
+^^^^^^^^^^^^^^^
+
+This feature was requested in `GitHub #280 `_.
+
+If you want to add a header to your downstream response please add the following to a Route in ocelot.json..
+
+.. code-block:: json
+
+ "DownstreamHeaderTransform": {
+ "Uncle": "Bob"
+ },
+
+In the example above a header with the key Uncle and value Bob would be returned by Ocelot when requesting the specific Route.
+
+If you want to return the Butterfly APM trace id then do something like the following..
+
+.. code-block:: json
+
+ "DownstreamHeaderTransform": {
+ "AnyKey": "{TraceId}"
+ },
+
+Find and Replace
+^^^^^^^^^^^^^^^^
+
+In order to transform a header first we specify the header key and then the type of transform we want e.g.
+
+.. code-block:: json
+
+ "Test": "http://www.bbc.co.uk/, http://ocelot.com/"
+
+The key is "Test" and the value is "http://www.bbc.co.uk/, http://ocelot.com/". The value is saying replace http://www.bbc.co.uk/ with http://ocelot.com/. The syntax is {find}, {replace}. Hopefully pretty simple. There are examples below that explain more.
+
+Pre Downstream Request
+^^^^^^^^^^^^^^^^^^^^^^
+
+Add the following to a Route in ocelot.json in order to replace http://www.bbc.co.uk/ with http://ocelot.com/. This header will be changed before the request downstream and will be sent to the downstream server.
+
+.. code-block:: json
+
+ "UpstreamHeaderTransform": {
+ "Test": "http://www.bbc.co.uk/, http://ocelot.com/"
+ },
+
+Post Downstream Request
+^^^^^^^^^^^^^^^^^^^^^^^
+
+Add the following to a Route in ocelot.json in order to replace http://www.bbc.co.uk/ with http://ocelot.com/. This transformation will take place after Ocelot has received the response from the downstream service.
+
+.. code-block:: json
+
+ "DownstreamHeaderTransform": {
+ "Test": "http://www.bbc.co.uk/, http://ocelot.com/"
+ },
+
+Placeholders
+^^^^^^^^^^^^
+
+Ocelot allows placeholders that can be used in header transformation.
+
+{RemoteIpAddress} - This will find the clients IP address using _httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString() so you will get back some IP.
+{BaseUrl} - This will use Ocelot's base url e.g. http://localhost:5000 as its value.
+{DownstreamBaseUrl} - This will use the downstream services base url e.g. http://localhost:5000 as its value. This only works for DownstreamHeaderTransform at the moment.
+{TraceId} - This will use the Butterfly APM Trace Id. This only works for DownstreamHeaderTransform at the moment.
+{UpstreamHost} - This will look for the incoming Host header.
+
+Handling 302 Redirects
+^^^^^^^^^^^^^^^^^^^^^^
+Ocelot will by default automatically follow redirects however if you want to return the location header to the client you might want to change the location to be Ocelot not the downstream service. Ocelot allows this with the following configuration.
+
+.. code-block:: json
+
+ "DownstreamHeaderTransform": {
+ "Location": "http://www.bbc.co.uk/, http://ocelot.com/"
+ },
+ "HttpHandlerOptions": {
+ "AllowAutoRedirect": false,
+ },
+
+or you could use the BaseUrl placeholder.
+
+.. code-block:: json
+
+ "DownstreamHeaderTransform": {
+ "Location": "http://localhost:6773, {BaseUrl}"
+ },
+ "HttpHandlerOptions": {
+ "AllowAutoRedirect": false,
+ },
+
+finally if you are using a load balancer with Ocelot you will get multiple downstream base urls so the above would not work. In this case you can do the following.
+
+.. code-block:: json
+
+ "DownstreamHeaderTransform": {
+ "Location": "{DownstreamBaseUrl}, {BaseUrl}"
+ },
+ "HttpHandlerOptions": {
+ "AllowAutoRedirect": false,
+ },
+
+X-Forwarded-For
+^^^^^^^^^^^^^^^
+
+An example of using {RemoteIpAddress} placeholder...
+
+.. code-block:: json
+
+ "UpstreamHeaderTransform": {
+ "X-Forwarded-For": "{RemoteIpAddress}"
+ }
+
+Future
+^^^^^^
+
+Ideally this feature would be able to support the fact that a header can have multiple values. At the moment it just assumes one. It would also be nice if it could multi find and replace e.g.
+
+.. code-block:: json
+
+ "DownstreamHeaderTransform": {
+ "Location": "[{one,one},{two,two}"
+ },
+ "HttpHandlerOptions": {
+ "AllowAutoRedirect": false,
+ },
+
+If anyone wants to have a go at this please help yourself!!
diff --git a/docs/features/kubernetes.rst b/docs/features/kubernetes.rst
index 75568ec41..abab44aef 100644
--- a/docs/features/kubernetes.rst
+++ b/docs/features/kubernetes.rst
@@ -1,95 +1,95 @@
-Kubernetes
-==============
-
-This feature was requested as part of `Issue 345 `_ . to add support for kubernetes's service discovery provider.
-
-The first thing you need to do is install the NuGet package that provides kubernetes support in Ocelot.
-
-``Install-Package Ocelot.Provider.Kubernetes``
-
-Then add the following to your ConfigureServices method.
-
-.. code-block:: csharp
-
- s.AddOcelot()
- .AddKubernetes();
-
-If you have services deployed in kubernetes you will normally use the naming service to access them. Default usePodServiceAccount = True, which means that ServiceAccount using Pod to access the service of the k8s cluster needs to be ServiceAccount based on RABC authorization
-
-.. code-block::csharp
- public static class OcelotBuilderExtensions
- {
- public static IOcelotBuilder AddKubernetes(this IOcelotBuilder builder, bool usePodServiceAccount = true);
- }
-
-You can replicate a Permissive. Using RBAC role bindings.
-`Permissive RBAC Permissions `_, k8s api server and token will read from pod .
-
-.. code-block::bash
-kubectl create clusterrolebinding permissive-binding --clusterrole=cluster-admin --user=admin --user=kubelet --group=system:serviceaccounts
-
-The following example shows how to set up a ReRoute that will work in kubernetes. The most important thing is the ServiceName which is made up of the
-kubernetes service name. We also need to set up the ServiceDiscoveryProvider in GlobalConfiguration. The example here shows a typical configuration.
-
-
-.. code-block:: json
-
- {
- "ReRoutes": [
- {
- "DownstreamPathTemplate": "/api/values",
- "DownstreamScheme": "http",
- "UpstreamPathTemplate": "/values",
- "ServiceName": "downstreamservice",
- "UpstreamHttpMethod": [ "Get" ]
- }
- ],
- "GlobalConfiguration": {
- "ServiceDiscoveryProvider": {
- "Host": "192.168.0.13",
- "Port": 443,
- "Token": "txpc696iUhbVoudg164r93CxDTrKRVWG",
- "Namespace": "dev",
- "Type": "kube"
- }
- }
-}
-
-Service deployment in Namespace Dev , ServiceDiscoveryProvider type is kube, you also can set pollkube ServiceDiscoveryProvider type.
- Note: Host、 Port and Token are no longer in use。
-
-You use Ocelot to poll kubernetes for latest service information rather than per request. If you want to poll kubernetes for the latest services rather than per request (default behaviour) then you need to set the following configuration.
-
-.. code-block:: json
-
- "ServiceDiscoveryProvider": {
- "Host": "192.168.0.13",
- "Port": 443,
- "Token": "txpc696iUhbVoudg164r93CxDTrKRVWG",
- "Namespace": "dev",
- "Type": "pollkube"
- "PollingInterval": 100
- }
-
-The polling interval is in milliseconds and tells Ocelot how often to call kubernetes for changes in service configuration.
-
-Please note there are tradeoffs here. If you poll kubernetes it is possible Ocelot will not know if a service is down depending on your polling interval and you might get more errors than if you get the latest services per request. This really depends on how volatile your services are. I doubt it will matter for most people and polling may give a tiny performance improvement over calling kubernetes per request.
-There is no way for Ocelot to work these out for you.
-
-If your downstream service resides in a different namespace you can override the global setting at the ReRoute level by specifying a ServiceNamespace
-
-
-.. code-block:: json
-
-{
- "ReRoutes": [
- {
- "DownstreamPathTemplate": "/api/values",
- "DownstreamScheme": "http",
- "UpstreamPathTemplate": "/values",
- "ServiceName": "downstreamservice",
- "ServiceNamespace": "downstream-namespace",
- "UpstreamHttpMethod": [ "Get" ]
- }
- ]
-}
+Kubernetes
+==============
+
+This feature was requested as part of `Issue 345 `_ . to add support for kubernetes's provider.
+
+Ocelot will call the k8s endpoints API in a given namespace to get all of the endpoints for a pod and then load balance across them. Ocelot used to use the services api to send requests to the k8s service but this was changed in `PR 1134 `_ because the service did not load balance as expected.
+
+The first thing you need to do is install the NuGet package that provides kubernetes support in Ocelot.
+
+``Install-Package Ocelot.Provider.Kubernetes``
+
+Then add the following to your ConfigureServices method.
+
+.. code-block:: csharp
+
+ s.AddOcelot()
+ .AddKubernetes();
+
+If you have services deployed in kubernetes you will normally use the naming service to access them. Default usePodServiceAccount = True, which means that ServiceAccount using Pod to access the service of the k8s cluster needs to be ServiceAccount based on RBAC authorization
+
+.. code-block::csharp
+ public static class OcelotBuilderExtensions
+ {
+ public static IOcelotBuilder AddKubernetes(this IOcelotBuilder builder, bool usePodServiceAccount = true);
+ }
+
+You can replicate a Permissive. Using RBAC role bindings.
+`Permissive RBAC Permissions `_, k8s api server and token will read from pod.
+
+.. code-block::bash
+kubectl create clusterrolebinding permissive-binding --clusterrole=cluster-admin --user=admin --user=kubelet --group=system:serviceaccounts
+
+The following example shows how to set up a Route that will work in kubernetes. The most important thing is the ServiceName which is made up of the kubernetes service name. We also need to set up the ServiceDiscoveryProvider in GlobalConfiguration. The example here shows a typical configuration.
+
+
+.. code-block:: json
+
+ {
+ "Routes": [
+ {
+ "DownstreamPathTemplate": "/api/values",
+ "DownstreamScheme": "http",
+ "UpstreamPathTemplate": "/values",
+ "ServiceName": "downstreamservice",
+ "UpstreamHttpMethod": [ "Get" ]
+ }
+ ],
+ "GlobalConfiguration": {
+ "ServiceDiscoveryProvider": {
+ "Host": "192.168.0.13",
+ "Port": 443,
+ "Token": "txpc696iUhbVoudg164r93CxDTrKRVWG",
+ "Namespace": "dev",
+ "Type": "kube"
+ }
+ }
+}
+
+Service deployment in Namespace Dev , ServiceDiscoveryProvider type is kube, you also can set pollkube ServiceDiscoveryProvider type.
+ Note: Host、 Port and Token are no longer in use。
+
+You use Ocelot to poll kubernetes for latest service information rather than per request. If you want to poll kubernetes for the latest services rather than per request (default behaviour) then you need to set the following configuration.
+
+.. code-block:: json
+
+ "ServiceDiscoveryProvider": {
+ "Host": "192.168.0.13",
+ "Port": 443,
+ "Token": "txpc696iUhbVoudg164r93CxDTrKRVWG",
+ "Namespace": "dev",
+ "Type": "pollkube",
+ "PollingInterval": 100
+ }
+
+The polling interval is in milliseconds and tells Ocelot how often to call kubernetes for changes in service configuration.
+
+Please note there are tradeoffs here. If you poll kubernetes it is possible Ocelot will not know if a service is down depending on your polling interval and you might get more errors than if you get the latest services per request. This really depends on how volatile your services are. I doubt it will matter for most people and polling may give a tiny performance improvement over calling kubernetes per request. There is no way for Ocelot to work these out for you.
+
+If your downstream service resides in a different namespace you can override the global setting at the Route level by specifying a ServiceNamespace.
+
+
+.. code-block:: json
+
+ {
+ "Routes": [
+ {
+ "DownstreamPathTemplate": "/api/values",
+ "DownstreamScheme": "http",
+ "UpstreamPathTemplate": "/values",
+ "ServiceName": "downstreamservice",
+ "ServiceNamespace": "downstream-namespace",
+ "UpstreamHttpMethod": [ "Get" ]
+ }
+ ]
+ }
diff --git a/docs/features/loadbalancer.rst b/docs/features/loadbalancer.rst
index 707060338..a4ec4ad91 100644
--- a/docs/features/loadbalancer.rst
+++ b/docs/features/loadbalancer.rst
@@ -1,110 +1,210 @@
-Load Balancer
-=============
-
-Ocelot can load balance across available downstream services for each ReRoute. This means you can scale your downstream services and Ocelot can use them effectively.
-
-The type of load balancer available are:
-
- LeastConnection - tracks which services are dealing with requests and sends new requests to service with least existing requests. The algorythm state is not distributed across a cluster of Ocelot's.
-
- RoundRobin - loops through available services and sends requests. The algorythm state is not distributed across a cluster of Ocelot's.
-
- NoLoadBalancer - takes the first available service from config or service discovery.
-
- CookieStickySessions - uses a cookie to stick all requests to a specific server. More info below.
-
-You must choose in your configuration which load balancer to use.
-
-Configuration
-^^^^^^^^^^^^^
-
-The following shows how to set up multiple downstream services for a ReRoute using ocelot.json and then select the LeastConnection load balancer. This is the simplest way to get load balancing set up.
-
-.. code-block:: json
-
- {
- "DownstreamPathTemplate": "/api/posts/{postId}",
- "DownstreamScheme": "https",
- "DownstreamHostAndPorts": [
- {
- "Host": "10.0.1.10",
- "Port": 5000,
- },
- {
- "Host": "10.0.1.11",
- "Port": 5000,
- }
- ],
- "UpstreamPathTemplate": "/posts/{postId}",
- "LoadBalancerOptions": {
- "Type": "LeastConnection"
- },
- "UpstreamHttpMethod": [ "Put", "Delete" ]
- }
-
-
-Service Discovery
-^^^^^^^^^^^^^^^^^
-
-The following shows how to set up a ReRoute using service discovery then select the LeastConnection load balancer.
-
-.. code-block:: json
-
- {
- "DownstreamPathTemplate": "/api/posts/{postId}",
- "DownstreamScheme": "https",
- "UpstreamPathTemplate": "/posts/{postId}",
- "UpstreamHttpMethod": [ "Put" ],
- "ServiceName": "product",
- "LoadBalancerOptions": {
- "Type": "LeastConnection"
- },
- }
-
-When this is set up Ocelot will lookup the downstream host and port from the service discover provider and load balance requests across any available services. If you add and remove services from the
-service discovery provider (consul) then Ocelot should respect this and stop calling services that have been removed and start calling services that have been added.
-
-CookieStickySessions
-^^^^^^^^^^^^^^^^^^^^
-
-I've implemented a really basic sticky session type of load balancer. The scenario it is meant to support is you have a bunch of downstream
-servers that don't share session state so if you get more than one request for one of these servers then it should go to the same box each
-time or the session state might be incorrect for the given user. This feature was requested in `Issue #322 `_
-though what the user wants is more complicated than just sticky sessions :) anyway I thought this would be a nice feature to have!
-
-In order to set up CookieStickySessions load balancer you need to do something like the following.
-
-.. code-block:: json
-
- {
- "DownstreamPathTemplate": "/api/posts/{postId}",
- "DownstreamScheme": "https",
- "DownstreamHostAndPorts": [
- {
- "Host": "10.0.1.10",
- "Port": 5000,
- },
- {
- "Host": "10.0.1.11",
- "Port": 5000,
- }
- ],
- "UpstreamPathTemplate": "/posts/{postId}",
- "LoadBalancerOptions": {
- "Type": "CookieStickySessions",
- "Key": "ASP.NET_SessionId",
- "Expiry": 1800000
- },
- "UpstreamHttpMethod": [ "Put", "Delete" ]
- }
-
-The LoadBalancerOptions are Type this needs to be CookieStickySessions, Key this is the key of the cookie you
-wish to use for the sticky sessions, Expiry this is how long in milliseconds you want to the session to be stuck for. Remember this
-refreshes on every request which is meant to mimick how sessions work usually.
-
-If you have multiple ReRoutes with the same LoadBalancerOptions then all of those ReRoutes will use the same load balancer for there
-subsequent requests. This means the sessions will be stuck across ReRoutes.
-
-Please note that if you give more than one DownstreamHostAndPort or you are using a Service Discovery provider such as Consul
-and this returns more than one service then CookieStickySessions uses round robin to select the next server. This is hard coded at the
-moment but could be changed.
+Load Balancer
+=============
+
+Ocelot can load balance across available downstream services for each Route. This means you can scale your downstream services and Ocelot can use them effectively.
+
+The type of load balancer available are:
+
+ LeastConnection - tracks which services are dealing with requests and sends new requests to service with least existing requests. The algorythm state is not distributed across a cluster of Ocelot's.
+
+ RoundRobin - loops through available services and sends requests. The algorythm state is not distributed across a cluster of Ocelot's.
+
+ NoLoadBalancer - takes the first available service from config or service discovery.
+
+ CookieStickySessions - uses a cookie to stick all requests to a specific server. More info below.
+
+You must choose in your configuration which load balancer to use.
+
+Configuration
+^^^^^^^^^^^^^
+
+The following shows how to set up multiple downstream services for a Route using ocelot.json and then select the LeastConnection load balancer. This is the simplest way to get load balancing set up.
+
+.. code-block:: json
+
+ {
+ "DownstreamPathTemplate": "/api/posts/{postId}",
+ "DownstreamScheme": "https",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "10.0.1.10",
+ "Port": 5000,
+ },
+ {
+ "Host": "10.0.1.11",
+ "Port": 5000,
+ }
+ ],
+ "UpstreamPathTemplate": "/posts/{postId}",
+ "LoadBalancerOptions": {
+ "Type": "LeastConnection"
+ },
+ "UpstreamHttpMethod": [ "Put", "Delete" ]
+ }
+
+
+Service Discovery
+^^^^^^^^^^^^^^^^^
+
+The following shows how to set up a Route using service discovery then select the LeastConnection load balancer.
+
+.. code-block:: json
+
+ {
+ "DownstreamPathTemplate": "/api/posts/{postId}",
+ "DownstreamScheme": "https",
+ "UpstreamPathTemplate": "/posts/{postId}",
+ "UpstreamHttpMethod": [ "Put" ],
+ "ServiceName": "product",
+ "LoadBalancerOptions": {
+ "Type": "LeastConnection"
+ },
+ }
+
+When this is set up Ocelot will lookup the downstream host and port from the service discover provider and load balance requests across any available services. If you add and remove services from the
+service discovery provider (consul) then Ocelot should respect this and stop calling services that have been removed and start calling services that have been added.
+
+CookieStickySessions
+^^^^^^^^^^^^^^^^^^^^
+
+I've implemented a really basic sticky session type of load balancer. The scenario it is meant to support is you have a bunch of downstream servers that don't share session state so if you get more than one request for one of these servers then it should go to the same box each time or the session state might be incorrect for the given user. This feature was requested in `Issue #322 `_ though what the user wants is more complicated than just sticky sessions :) anyway I thought this would be a nice feature to have!
+
+In order to set up CookieStickySessions load balancer you need to do something like the following.
+
+.. code-block:: json
+
+ {
+ "DownstreamPathTemplate": "/api/posts/{postId}",
+ "DownstreamScheme": "https",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "10.0.1.10",
+ "Port": 5000,
+ },
+ {
+ "Host": "10.0.1.11",
+ "Port": 5000,
+ }
+ ],
+ "UpstreamPathTemplate": "/posts/{postId}",
+ "LoadBalancerOptions": {
+ "Type": "CookieStickySessions",
+ "Key": "ASP.NET_SessionId",
+ "Expiry": 1800000
+ },
+ "UpstreamHttpMethod": [ "Put", "Delete" ]
+ }
+
+The LoadBalancerOptions are Type this needs to be CookieStickySessions, Key this is the key of the cookie you wish to use for the sticky sessions, Expiry this is how long in milliseconds you want to the session to be stuck for. Remember this refreshes on every request which is meant to mimick how sessions work usually.
+
+If you have multiple Routes with the same LoadBalancerOptions then all of those Routes will use the same load balancer for there subsequent requests. This means the sessions will be stuck across Routes.
+
+Please note that if you give more than one DownstreamHostAndPort or you are using a Service Discovery provider such as Consul and this returns more than one service then CookieStickySessions uses round robin to select the next server. This is hard coded at the moment but could be changed.
+
+Custom Load Balancers
+^^^^^^^^^^^^^^^^^^^^
+
+`DavidLievrouw >> _services;
+ private readonly object _lock = new object();
+
+ private int _last;
+
+ public CustomLoadBalancer(Func>> services)
+ {
+ _services = services;
+ }
+
+ public async Task> Lease(DownstreamContext downstreamContext, HttpContext httpContext)
+ {
+ var services = await _services();
+ lock (_lock)
+ {
+ if (_last >= services.Count)
+ {
+ _last = 0;
+ }
+
+ var next = services[_last];
+ _last++;
+ return new OkResponse(next.HostAndPort);
+ }
+ }
+
+ public void Release(ServiceHostAndPort hostAndPort)
+ {
+ }
+ }
+
+Finally you need to register this class with Ocelot. I have used the most complex example below to show all of the data / types that can be passed into the factory that creates load balancers.
+
+.. code-block:: csharp
+
+ Func loadBalancerFactoryFunc = (serviceProvider, Route, serviceDiscoveryProvider) => new CustomLoadBalancer(serviceDiscoveryProvider.Get);
+
+ s.AddOcelot()
+ .AddCustomLoadBalancer(loadBalancerFactoryFunc);
+
+However there is a much simpler example that will work the same.
+
+.. code-block:: csharp
+
+ s.AddOcelot()
+ .AddCustomLoadBalancer();
+
+There are numerous extension methods to add a custom load balancer and the interface is as follows.
+
+.. code-block:: csharp
+
+ IOcelotBuilder AddCustomLoadBalancer()
+ where T : ILoadBalancer, new();
+
+ IOcelotBuilder AddCustomLoadBalancer(Func loadBalancerFactoryFunc)
+ where T : ILoadBalancer;
+
+ IOcelotBuilder AddCustomLoadBalancer(Func loadBalancerFactoryFunc)
+ where T : ILoadBalancer;
+
+ IOcelotBuilder AddCustomLoadBalancer(
+ Func loadBalancerFactoryFunc)
+ where T : ILoadBalancer;
+
+ IOcelotBuilder AddCustomLoadBalancer(
+ Func loadBalancerFactoryFunc)
+ where T : ILoadBalancer;
+
+When you enable custom load balancers Ocelot looks up your load balancer by its class name when it decides if it should do load balancing. If it finds a match it will use your load balaner to load balance. If Ocelot cannot match the load balancer type in your configuration with the name of registered load balancer class then you will receive a HTTP 500 internal server error. If your load balancer factory throw an exception when Ocelot calls it you will receive a HTTP 500 internal server error.
+
+Remember if you specify no load balancer in your config Ocelot will not try and load balance.
\ No newline at end of file
diff --git a/docs/features/logging.rst b/docs/features/logging.rst
index b09a26cfb..c79c7743d 100644
--- a/docs/features/logging.rst
+++ b/docs/features/logging.rst
@@ -1,20 +1,17 @@
Logging
=======
-Ocelot uses the standard logging interfaces ILoggerFactory / ILogger at the moment.
-This is encapsulated in IOcelotLogger / IOcelotLoggerFactory with an implementation
+Ocelot uses the standard logging interfaces ILoggerFactory / ILogger at the moment. This is encapsulated in IOcelotLogger / IOcelotLoggerFactory with an implementation
for the standard asp.net core logging stuff at the moment. This is because Ocelot add's some extra info to the logs such as request id if it is configured.
There is a global error handler that should catch any exceptions thrown and log them as errors.
Finally if logging is set to trace level Ocelot will log starting, finishing and any middlewares that throw an exception which can be quite useful.
-The reason for not just using bog standard framework logging is that I could not
-work out how to override the request id that get's logged when setting IncludeScopes
+The reason for not just using bog standard framework logging is that I could not work out how to override the request id that get's logged when setting IncludeScopes
to true for logging settings. Nicely onto the next feature.
Warning
^^^^^^^
-If you are logging to Console you will get terrible performance. I have had so many issues about performance issues with Ocelot
-and it is always logging level Debug, logging to Console :) Make sure you are logging to something proper in production :)
+If you are logging to Console you will get terrible performance. I have had so many issues about performance issues with Ocelot and it is always logging level Debug, logging to Console :) Make sure you are logging to something proper in production :)
diff --git a/docs/features/methodtransformation.rst b/docs/features/methodtransformation.rst
new file mode 100644
index 000000000..b1fc8cd32
--- /dev/null
+++ b/docs/features/methodtransformation.rst
@@ -0,0 +1,28 @@
+HTTP Method Transformation
+==========================
+
+Ocelot allows the user to change the HTTP request method that will be used when making a request to a downstream service.
+
+This achieved by setting the following Route configuration:
+
+.. code-block:: json
+
+ {
+ "DownstreamPathTemplate": "/{url}",
+ "UpstreamPathTemplate": "/{url}",
+ "UpstreamHttpMethod": [
+ "Get"
+ ],
+ "DownstreamHttpMethod": "POST",
+ "DownstreamScheme": "http",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "localhost",
+ "Port": 53271
+ }
+ ],
+ }
+
+The key property here is DownstreamHttpMethod which is set as POST and the Route will only match on GET as set by UpstreamHttpMethod.
+
+This feature can be useful when interacting with downstream apis that only support POST and you want to present some kind of RESTful interface.
\ No newline at end of file
diff --git a/docs/features/middlewareinjection.rst b/docs/features/middlewareinjection.rst
index 4b6636582..7fccc599b 100644
--- a/docs/features/middlewareinjection.rst
+++ b/docs/features/middlewareinjection.rst
@@ -31,9 +31,9 @@ The user can set functions against the following.
* AuthenticationMiddleware - This overrides Ocelots authentication middleware.
-* PreAuthorisationMiddleware - This allows the user to run pre authorisation logic and then call Ocelot's authorisation middleware.
+* PreAuthorizationMiddleware - This allows the user to run pre authorization logic and then call Ocelot's authorization middleware.
-* AuthorisationMiddleware - This overrides Ocelots authorisation middleware.
+* AuthorizationMiddleware - This overrides Ocelots authorization middleware.
* PreQueryStringBuilderMiddleware - This allows the user to manipulate the query string on the http request before it is passed to Ocelots request creator.
diff --git a/docs/features/qualityofservice.rst b/docs/features/qualityofservice.rst
index 9839b2f76..ddd4d6892 100644
--- a/docs/features/qualityofservice.rst
+++ b/docs/features/qualityofservice.rst
@@ -1,8 +1,7 @@
Quality of Service
==================
-Ocelot supports one QoS capability at the current time. You can set on a per ReRoute basis if you
-want to use a circuit breaker when making requests to a downstream service. This uses an awesome
+Ocelot supports one QoS capability at the current time. You can set on a per Route basis if you want to use a circuit breaker when making requests to a downstream service. This uses an awesome
.NET library called Polly check them out `here `_.
The first thing you need to do if you want to use the administration API is bring in the relevant NuGet package..
@@ -20,7 +19,7 @@ Then in your ConfigureServices method
.AddPolly();
}
-Then add the following section to a ReRoute configuration.
+Then add the following section to a Route configuration.
.. code-block:: json
@@ -30,8 +29,7 @@ Then add the following section to a ReRoute configuration.
"TimeoutValue":5000
}
-You must set a number greater than 0 against ExceptionsAllowedBeforeBreaking for this rule to be
-implemented. Duration of break means the circuit breaker will stay open for 1 second after it is tripped.
+You must set a number greater than 0 against ExceptionsAllowedBeforeBreaking for this rule to be implemented. Duration of break means the circuit breaker will stay open for 1 second after it is tripped.
TimeoutValue means if a request takes more than 5 seconds it will automatically be timed out.
You can set the TimeoutValue in isolation of the ExceptionsAllowedBeforeBreaking and DurationOfBreak options.
@@ -44,5 +42,4 @@ You can set the TimeoutValue in isolation of the ExceptionsAllowedBeforeBreaking
There is no point setting the other two in isolation as they affect each other :)
-If you do not add a QoS section QoS will not be used however Ocelot will default to a 90 second timeout
-on all downstream requests. If someone needs this to be configurable open an issue.
+If you do not add a QoS section QoS will not be used however Ocelot will default to a 90 second timeout on all downstream requests. If someone needs this to be configurable open an issue.
diff --git a/docs/features/raft.rst b/docs/features/raft.rst
deleted file mode 100644
index fdbc1ea24..000000000
--- a/docs/features/raft.rst
+++ /dev/null
@@ -1,49 +0,0 @@
-Raft (EXPERIMENTAL DO NOT USE IN PRODUCTION)
-============================================
-
-Ocelot has recently integrated `Rafty `_ which is an implementation of Raft that I have also been working on over the last year. This project is very experimental so please do not use this feature of Ocelot in production until I think it's OK.
-
-Raft is a distributed concensus algorithm that allows a cluster of servers (Ocelots) to maintain local state without having a centralised database for storing state (e.g. SQL Server).
-
-To get Raft support you must first install the Ocelot Rafty package.
-
-``Install-Package Ocelot.Provider.Rafty``
-
-Then you must make the following changes to your Startup.cs / Program.cs.
-
-.. code-block:: csharp
-
- public virtual void ConfigureServices(IServiceCollection services)
- {
- services
- .AddOcelot()
- .AddAdministration("/administration", "secret")
- .AddRafty();
- }
-
-In addition to this you must add a file called peers.json to your main project and it will look as follows
-
-.. code-block:: json
-
- {
- "Peers": [{
- "HostAndPort": "http://localhost:5000"
- },
- {
- "HostAndPort": "http://localhost:5002"
- },
- {
- "HostAndPort": "http://localhost:5003"
- },
- {
- "HostAndPort": "http://localhost:5004"
- },
- {
- "HostAndPort": "http://localhost:5001"
- }
- ]
- }
-
-Each instance of Ocelot must have it's address in the array so that they can communicate using Rafty.
-
-Once you have made these configuration changes you must deploy and start each instance of Ocelot using the addresses in the peers.json file. The servers should then start communicating with each other! You can test if everything is working by posting a configuration update and checking it has replicated to all servers by getting their configuration.
diff --git a/docs/features/ratelimiting.rst b/docs/features/ratelimiting.rst
index 4dd0354f7..895b3e419 100644
--- a/docs/features/ratelimiting.rst
+++ b/docs/features/ratelimiting.rst
@@ -1,47 +1,47 @@
-Rate Limiting
-=============
-
-Thanks to `@catcherwong article `_ for inspiring me to finally write this documentation.
-
-Ocelot supports rate limiting of upstream requests so that your downstream services do not become overloaded. This feature was added by @geffzhang on GitHub! Thanks very much.
-
-OK so to get rate limiting working for a ReRoute you need to add the following json to it.
-
-.. code-block:: json
-
- "RateLimitOptions": {
- "ClientWhitelist": [],
- "EnableRateLimiting": true,
- "Period": "1s",
- "PeriodTimespan": 1,
- "Limit": 1
- }
-
-ClientWhitelist - This is an array that contains the whitelist of the client. It means that the client in this array will not be affected by the rate limiting.
-
-EnableRateLimiting - This value specifies enable endpoint rate limiting.
-
-Period - This value specifies the period that the limit applies to, such as 1s, 5m, 1h,1d and so on. If you make more requests in the period than the limit allows then you need to wait for PeriodTimespan to elapse before you make another request.
-
-PeriodTimespan - This value specifies that we can retry after a certain number of seconds.
-
-Limit - This value specifies the maximum number of requests that a client can make in a defined period.
-
-You can also set the following in the GlobalConfiguration part of ocelot.json
-
-.. code-block:: json
-
- "RateLimitOptions": {
- "DisableRateLimitHeaders": false,
- "QuotaExceededMessage": "Customize Tips!",
- "HttpStatusCode": 999,
- "ClientIdHeader" : "Test"
- }
-
-DisableRateLimitHeaders - This value specifies whether X-Rate-Limit and Retry-After headers are disabled.
-
-QuotaExceededMessage - This value specifies the exceeded message.
-
-HttpStatusCode - This value specifies the returned HTTP Status code when rate limiting occurs.
-
-ClientIdHeader - Allows you to specifiy the header that should be used to identify clients. By default it is "ClientId"
+Rate Limiting
+=============
+
+Thanks to `@catcherwong article `_ for inspiring me to finally write this documentation.
+
+Ocelot supports rate limiting of upstream requests so that your downstream services do not become overloaded. This feature was added by @geffzhang on GitHub! Thanks very much.
+
+OK so to get rate limiting working for a Route you need to add the following json to it.
+
+.. code-block:: json
+
+ "RateLimitOptions": {
+ "ClientWhitelist": [],
+ "EnableRateLimiting": true,
+ "Period": "1s",
+ "PeriodTimespan": 1,
+ "Limit": 1
+ }
+
+ClientWhitelist - This is an array that contains the whitelist of the client. It means that the client in this array will not be affected by the rate limiting.
+
+EnableRateLimiting - This value specifies enable endpoint rate limiting.
+
+Period - This value specifies the period that the limit applies to, such as 1s, 5m, 1h,1d and so on. If you make more requests in the period than the limit allows then you need to wait for PeriodTimespan to elapse before you make another request.
+
+PeriodTimespan - This value specifies that we can retry after a certain number of seconds.
+
+Limit - This value specifies the maximum number of requests that a client can make in a defined period.
+
+You can also set the following in the GlobalConfiguration part of ocelot.json
+
+.. code-block:: json
+
+ "RateLimitOptions": {
+ "DisableRateLimitHeaders": false,
+ "QuotaExceededMessage": "Customize Tips!",
+ "HttpStatusCode": 999,
+ "ClientIdHeader" : "Test"
+ }
+
+DisableRateLimitHeaders - This value specifies whether X-Rate-Limit and Retry-After headers are disabled.
+
+QuotaExceededMessage - This value specifies the exceeded message.
+
+HttpStatusCode - This value specifies the returned HTTP Status code when rate limiting occurs.
+
+ClientIdHeader - Allows you to specifiy the header that should be used to identify clients. By default it is "ClientId"
diff --git a/docs/features/requestaggregation.rst b/docs/features/requestaggregation.rst
index 6281728cd..3c84d0da5 100644
--- a/docs/features/requestaggregation.rst
+++ b/docs/features/requestaggregation.rst
@@ -1,187 +1,185 @@
-Request Aggregation
-===================
-
-Ocelot allows you to specify Aggregate ReRoutes that compose multiple normal ReRoutes and map their responses into one object. This is usually where you have
-a client that is making multiple requests to a server where it could just be one. This feature allows you to start implementing back end for a front end type
-architecture with Ocelot.
-
-This feature was requested as part of `Issue 79 `_ and further improvements were made as part of `Issue 298 `_.
-
-In order to set this up you must do something like the following in your ocelot.json. Here we have specified two normal ReRoutes and each one has a Key property.
-We then specify an Aggregate that composes the two ReRoutes using their keys in the ReRouteKeys list and says then we have the UpstreamPathTemplate which works like a normal ReRoute.
-Obviously you cannot have duplicate UpstreamPathTemplates between ReRoutes and Aggregates. You can use all of Ocelot's normal ReRoute options apart from RequestIdKey (explained in gotchas below).
-
-Advanced register your own Aggregators
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Ocelot started with just the basic request aggregation and since then we have added a more advanced method that let's the user take in the responses from the
-downstream services and then aggregate them into a response object.
-
-The ocelot.json setup is pretty much the same as the basic aggregation approach apart from you need to add an Aggregator property like below.
-
-.. code-block:: json
-
- {
- "ReRoutes": [
- {
- "DownstreamPathTemplate": "/",
- "UpstreamPathTemplate": "/laura",
- "UpstreamHttpMethod": [
- "Get"
- ],
- "DownstreamScheme": "http",
- "DownstreamHostAndPorts": [
- {
- "Host": "localhost",
- "Port": 51881
- }
- ],
- "Key": "Laura"
- },
- {
- "DownstreamPathTemplate": "/",
- "UpstreamPathTemplate": "/tom",
- "UpstreamHttpMethod": [
- "Get"
- ],
- "DownstreamScheme": "http",
- "DownstreamHostAndPorts": [
- {
- "Host": "localhost",
- "Port": 51882
- }
- ],
- "Key": "Tom"
- }
- ],
- "Aggregates": [
- {
- "ReRouteKeys": [
- "Tom",
- "Laura"
- ],
- "UpstreamPathTemplate": "/",
- "Aggregator": "FakeDefinedAggregator"
- }
- ]
- }
-
-Here we have added an aggregator called FakeDefinedAggregator. Ocelot is going to look for this aggregator when it tries to aggregate this ReRoute.
-
-In order to make the aggregator available we must add the FakeDefinedAggregator to the OcelotBuilder like below.
-
-.. code-block:: csharp
-
- services
- .AddOcelot()
- .AddSingletonDefinedAggregator();
-
-Now when Ocelot tries to aggregate the ReRoute above it will find the FakeDefinedAggregator in the container and use it to aggregate the ReRoute.
-Because the FakeDefinedAggregator is registered in the container you can add any dependencies it needs into the container like below.
-
-.. code-block:: csharp
-
- services.AddSingleton();
-
- services
- .AddOcelot()
- .AddSingletonDefinedAggregator();
-
-In this example FooAggregator takes a dependency on FooDependency and it will be resolved by the container.
-
-In addition to this Ocelot lets you add transient aggregators like below.
-
-.. code-block:: csharp
-
- services
- .AddOcelot()
- .AddTransientDefinedAggregator();
-
-In order to make an Aggregator you must implement this interface.
-
-.. code-block:: csharp
-
- public interface IDefinedAggregator
- {
- Task Aggregate(List responses);
- }
-
-With this feature you can pretty much do whatever you want because DownstreamResponse contains Content, Headers and Status Code. We can add extra things if needed
-just raise an issue on GitHub. Please note if the HttpClient throws an exception when making a request to a ReRoute in the aggregate then you will not get a DownstreamResponse for
-it but you would for any that succeed. If it does throw an exception this will be logged.
-
-Basic expecting JSON from Downstream Services
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-.. code-block:: json
-
- {
- "ReRoutes": [
- {
- "DownstreamPathTemplate": "/",
- "UpstreamPathTemplate": "/laura",
- "UpstreamHttpMethod": [
- "Get"
- ],
- "DownstreamScheme": "http",
- "DownstreamHostAndPorts": [
- {
- "Host": "localhost",
- "Port": 51881
- }
- ],
- "Key": "Laura"
- },
- {
- "DownstreamPathTemplate": "/",
- "UpstreamPathTemplate": "/tom",
- "UpstreamHttpMethod": [
- "Get"
- ],
- "DownstreamScheme": "http",
- "DownstreamHostAndPorts": [
- {
- "Host": "localhost",
- "Port": 51882
- }
- ],
- "Key": "Tom"
- }
- ],
- "Aggregates": [
- {
- "ReRouteKeys": [
- "Tom",
- "Laura"
- ],
- "UpstreamPathTemplate": "/"
- }
- ]
- }
-
-You can also set UpstreamHost and ReRouteIsCaseSensitive in the Aggregate configuration. These behave the same as any other ReRoutes.
-
-If the ReRoute /tom returned a body of {"Age": 19} and /laura returned {"Age": 25} the the response after aggregation would be as follows.
-
-.. code-block:: json
-
- {"Tom":{"Age": 19},"Laura":{"Age": 25}}
-
-At the moment the aggregation is very simple. Ocelot just gets the response from your downstream service and sticks it into a json dictionary
-as above. With the ReRoute key being the key of the dictionary and the value the response body from your downstream service. You can see that the object is just
-JSON without any pretty spaces etc.
-
-All headers will be lost from the downstream services response.
-
-Ocelot will always return content type application/json with an aggregate request.
-
-If you downstream services return a 404 the aggregate will just return nothing for that downstream service.
-It will not change the aggregate response into a 404 even if all the downstreams return a 404.
-
-Gotcha's / Further info
------------------------
-
-You cannot use ReRoutes with specific RequestIdKeys as this would be crazy complicated to track.
-
-Aggregation only supports the GET HTTP Verb.
-
+Request Aggregation
+===================
+
+Ocelot allows you to specify Aggregate Routes that compose multiple normal Routes and map their responses into one object. This is usually where you have
+a client that is making multiple requests to a server where it could just be one. This feature allows you to start implementing back end for a front end type
+architecture with Ocelot.
+
+This feature was requested as part of `Issue 79 `_ and further improvements were made as part of `Issue 298 `_.
+
+In order to set this up you must do something like the following in your ocelot.json. Here we have specified two normal Routes and each one has a Key property.
+We then specify an Aggregate that composes the two Routes using their keys in the RouteKeys list and says then we have the UpstreamPathTemplate which works like a normal Route.
+Obviously you cannot have duplicate UpstreamPathTemplates between Routes and Aggregates. You can use all of Ocelot's normal Route options apart from RequestIdKey (explained in gotchas below).
+
+Advanced register your own Aggregators
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Ocelot started with just the basic request aggregation and since then we have added a more advanced method that let's the user take in the responses from the
+downstream services and then aggregate them into a response object.
+
+The ocelot.json setup is pretty much the same as the basic aggregation approach apart from you need to add an Aggregator property like below.
+
+.. code-block:: json
+
+ {
+ "Routes": [
+ {
+ "DownstreamPathTemplate": "/",
+ "UpstreamPathTemplate": "/laura",
+ "UpstreamHttpMethod": [
+ "Get"
+ ],
+ "DownstreamScheme": "http",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "localhost",
+ "Port": 51881
+ }
+ ],
+ "Key": "Laura"
+ },
+ {
+ "DownstreamPathTemplate": "/",
+ "UpstreamPathTemplate": "/tom",
+ "UpstreamHttpMethod": [
+ "Get"
+ ],
+ "DownstreamScheme": "http",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "localhost",
+ "Port": 51882
+ }
+ ],
+ "Key": "Tom"
+ }
+ ],
+ "Aggregates": [
+ {
+ "RouteKeys": [
+ "Tom",
+ "Laura"
+ ],
+ "UpstreamPathTemplate": "/",
+ "Aggregator": "FakeDefinedAggregator"
+ }
+ ]
+ }
+
+Here we have added an aggregator called FakeDefinedAggregator. Ocelot is going to look for this aggregator when it tries to aggregate this Route.
+
+In order to make the aggregator available we must add the FakeDefinedAggregator to the OcelotBuilder like below.
+
+.. code-block:: csharp
+
+ services
+ .AddOcelot()
+ .AddSingletonDefinedAggregator();
+
+Now when Ocelot tries to aggregate the Route above it will find the FakeDefinedAggregator in the container and use it to aggregate the Route.
+Because the FakeDefinedAggregator is registered in the container you can add any dependencies it needs into the container like below.
+
+.. code-block:: csharp
+
+ services.AddSingleton();
+
+ services
+ .AddOcelot()
+ .AddSingletonDefinedAggregator();
+
+In this example FooAggregator takes a dependency on FooDependency and it will be resolved by the container.
+
+In addition to this Ocelot lets you add transient aggregators like below.
+
+.. code-block:: csharp
+
+ services
+ .AddOcelot()
+ .AddTransientDefinedAggregator();
+
+In order to make an Aggregator you must implement this interface.
+
+.. code-block:: csharp
+
+ public interface IDefinedAggregator
+ {
+ Task Aggregate(List responses);
+ }
+
+With this feature you can pretty much do whatever you want because the HttpContext objects contain the results of all the aggregate requests. Please note if the HttpClient throws an exception when making a request to a Route in the aggregate then you will not get a HttpContext for it but you would for any that succeed. If it does throw an exception this will be logged.
+
+Basic expecting JSON from Downstream Services
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: json
+
+ {
+ "Routes": [
+ {
+ "DownstreamPathTemplate": "/",
+ "UpstreamPathTemplate": "/laura",
+ "UpstreamHttpMethod": [
+ "Get"
+ ],
+ "DownstreamScheme": "http",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "localhost",
+ "Port": 51881
+ }
+ ],
+ "Key": "Laura"
+ },
+ {
+ "DownstreamPathTemplate": "/",
+ "UpstreamPathTemplate": "/tom",
+ "UpstreamHttpMethod": [
+ "Get"
+ ],
+ "DownstreamScheme": "http",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "localhost",
+ "Port": 51882
+ }
+ ],
+ "Key": "Tom"
+ }
+ ],
+ "Aggregates": [
+ {
+ "RouteKeys": [
+ "Tom",
+ "Laura"
+ ],
+ "UpstreamPathTemplate": "/"
+ }
+ ]
+ }
+
+You can also set UpstreamHost and RouteIsCaseSensitive in the Aggregate configuration. These behave the same as any other Routes.
+
+If the Route /tom returned a body of {"Age": 19} and /laura returned {"Age": 25} the the response after aggregation would be as follows.
+
+.. code-block:: json
+
+ {"Tom":{"Age": 19},"Laura":{"Age": 25}}
+
+At the moment the aggregation is very simple. Ocelot just gets the response from your downstream service and sticks it into a json dictionary
+as above. With the Route key being the key of the dictionary and the value the response body from your downstream service. You can see that the object is just
+JSON without any pretty spaces etc.
+
+All headers will be lost from the downstream services response.
+
+Ocelot will always return content type application/json with an aggregate request.
+
+If you downstream services return a 404 the aggregate will just return nothing for that downstream service.
+It will not change the aggregate response into a 404 even if all the downstreams return a 404.
+
+Gotcha's / Further info
+-----------------------
+
+You cannot use Routes with specific RequestIdKeys as this would be crazy complicated to track.
+
+Aggregation only supports the GET HTTP Verb.
+
diff --git a/docs/features/requestid.rst b/docs/features/requestid.rst
index fb3f74cf5..d36734026 100644
--- a/docs/features/requestid.rst
+++ b/docs/features/requestid.rst
@@ -1,12 +1,10 @@
Request Id / Correlation Id
===========================
-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 supports a client sending a request id in the form of a header. If set Ocelot willuse the requestid for logging as soon as it becomes available in the middleware pipeline.
Ocelot will also forward the request id with the specified header to the downstream service.
-You can still get the asp.net core request id in the logs if you set
-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.
@@ -20,19 +18,19 @@ In your ocelot.json set the following in the GlobalConfiguration section. This w
"RequestIdKey": "OcRequestId"
}
-I recommend using the GlobalConfiguration unless you really need it to be ReRoute specific.
+I recommend using the GlobalConfiguration unless you really need it to be Route specific.
-*ReRoute*
+*Route*
-If you want to override this for a specific ReRoute add the following to ocelot.json for the specific ReRoute.
+If you want to override this for a specific Route add the following to ocelot.json for the specific Route.
.. code-block:: json
"RequestIdKey": "OcRequestId"
-Once Ocelot has identified the incoming requests matching ReRoute object it will set the request id based on the ReRoute configuration.
+Once Ocelot has identified the incoming requests matching Route object it will set the request id based on the Route configuration.
-This can lead to a small gotcha. If you set a GlobalConfiguration it is possible to get one request id until the ReRoute is identified and then another after that because the request id key can change. This is by design and is the best solution I can think of at the moment. In this case the OcelotLogger will show the request id and previous request id in the logs.
+This can lead to a small gotcha. If you set a GlobalConfiguration it is possible to get one request id until the Route is identified and then another after that because the request id key can change. This is by design and is the best solution I can think of at the moment. In this case the OcelotLogger will show the request id and previous request id in the logs.
Below is an example of the logging when set at Debug level for a normal request..
@@ -43,11 +41,11 @@ Below is an example of the logging when set at Debug level for a normal request.
dbug: Ocelot.DownstreamRouteFinder.Middleware.DownstreamRouteFinderMiddleware[0]
requestId: asdf, previousRequestId: no previous request id, message: upstream url path is {upstreamUrlPath},
dbug: Ocelot.DownstreamRouteFinder.Middleware.DownstreamRouteFinderMiddleware[0]
- requestId: asdf, previousRequestId: no previous request id, message: downstream template is {downstreamRoute.Data.ReRoute.DownstreamPath},
+ requestId: asdf, previousRequestId: no previous request id, message: downstream template is {downstreamRoute.Data.Route.DownstreamPath},
dbug: Ocelot.RateLimit.Middleware.ClientRateLimitMiddleware[0]
requestId: asdf, previousRequestId: no previous request id, message: EndpointRateLimiting is not enabled for Ocelot.Values.PathTemplate,
- dbug: Ocelot.Authorisation.Middleware.AuthorisationMiddleware[0]
- requestId: 1234, previousRequestId: asdf, message: /posts/{postId} route does not require user to be authorised,
+ dbug: Ocelot.Authorization.Middleware.AuthorizationMiddleware[0]
+ requestId: 1234, previousRequestId: asdf, message: /posts/{postId} route does not require user to be authorized,
dbug: Ocelot.DownstreamUrlCreator.Middleware.DownstreamUrlCreatorMiddleware[0]
requestId: 1234, previousRequestId: asdf, message: downstream url is {downstreamUrl.Data.Value},
dbug: Ocelot.Request.Middleware.HttpRequestBuilderMiddleware[0]
diff --git a/docs/features/routing.rst b/docs/features/routing.rst
index 18cc60c18..30e42a82b 100644
--- a/docs/features/routing.rst
+++ b/docs/features/routing.rst
@@ -1,245 +1,238 @@
-Routing
-=======
-
-Ocelot's primary functionality is to take incoming http requests and forward them on
-to a downstream service. Ocelot currently only supports this in the form of another http request (in the future
-this could be any transport mechanism).
-
-Ocelot's describes the routing of one request to another as a ReRoute. In order to get
-anything working in Ocelot you need to set up a ReRoute in the configuration.
-
-.. code-block:: json
-
- {
- "ReRoutes": [
- ]
- }
-
-To configure a ReRoute you need to add one to the ReRoutes json array.
-
-.. code-block:: json
-
- {
- "DownstreamPathTemplate": "/api/posts/{postId}",
- "DownstreamScheme": "https",
- "DownstreamHostAndPorts": [
- {
- "Host": "localhost",
- "Port": 80,
- }
- ],
- "UpstreamPathTemplate": "/posts/{postId}",
- "UpstreamHttpMethod": [ "Put", "Delete" ]
- }
-
-The DownstreamPathTemplate, DownstreamScheme and DownstreamHostAndPorts define the URL that a request will be forwarded to.
-
-DownstreamHostAndPorts is a collection that defines the host and port of any downstream services that you wish to forward requests to.
-Usually this will just contain a single entry but sometimes you might want to load balance requests to your downstream services and Ocelot allows you add more than one entry and then select a load balancer.
-
-The UpstreamPathTemplate is the URL that Ocelot will use to identify which DownstreamPathTemplate to use for a given request.
-The UpstreamHttpMethod is used so Ocelot can distinguish between requests with different HTTP verbs to the same URL. You can set a specific list of HTTP Methods or set an empty list to allow any of them.
-
-In Ocelot you can add placeholders for variables to your Templates in the form of {something}.
-The placeholder variable needs to be present in both the DownstreamPathTemplate and UpstreamPathTemplate properties. When it is Ocelot will attempt to substitute the value in the UpstreamPathTemplate placeholder into the DownstreamPathTemplate for each request Ocelot processes.
-
-You can also do a catch all type of ReRoute e.g.
-
-.. code-block:: json
-
- {
- "DownstreamPathTemplate": "/api/{everything}",
- "DownstreamScheme": "https",
- "DownstreamHostAndPorts": [
- {
- "Host": "localhost",
- "Port": 80,
- }
- ],
- "UpstreamPathTemplate": "/{everything}",
- "UpstreamHttpMethod": [ "Get", "Post" ]
- }
-
-This will forward any path + query string combinations to the downstream service after the path /api.
-
-
-The default ReRouting configuration is case insensitive!
-
-In order to change this you can specify on a per ReRoute basis the following setting.
-
-.. code-block:: json
-
- "ReRouteIsCaseSensitive": true
-
-This means that when Ocelot tries to match the incoming upstream url with an upstream template the
-evaluation will be case sensitive.
-
-Catch All
-^^^^^^^^^
-
-Ocelot's routing also supports a catch all style routing where the user can specify that they want to match all traffic.
-
-If you set up your config like below, all requests will be proxied straight through. The placeholder {url} name is not significant, any name will work.
-
-.. code-block:: json
-
- {
- "DownstreamPathTemplate": "/{url}",
- "DownstreamScheme": "https",
- "DownstreamHostAndPorts": [
- {
- "Host": "localhost",
- "Port": 80,
- }
- ],
- "UpstreamPathTemplate": "/{url}",
- "UpstreamHttpMethod": [ "Get" ]
- }
-
-The catch all has a lower priority than any other ReRoute. If you also have the ReRoute below in your config then Ocelot would match it before the catch all.
-
-.. code-block:: json
-
- {
- "DownstreamPathTemplate": "/",
- "DownstreamScheme": "https",
- "DownstreamHostAndPorts": [
- {
- "Host": "10.0.10.1",
- "Port": 80,
- }
- ],
- "UpstreamPathTemplate": "/",
- "UpstreamHttpMethod": [ "Get" ]
- }
-
-Upstream Host
-^^^^^^^^^^^^^
-
-This feature allows you to have ReRoutes based on the upstream host. This works by looking at the host header the client has used and then using this as part of the information we use to identify a ReRoute.
-
-In order to use this feature please add the following to your config.
-
-.. code-block:: json
-
- {
- "DownstreamPathTemplate": "/",
- "DownstreamScheme": "https",
- "DownstreamHostAndPorts": [
- {
- "Host": "10.0.10.1",
- "Port": 80,
- }
- ],
- "UpstreamPathTemplate": "/",
- "UpstreamHttpMethod": [ "Get" ],
- "UpstreamHost": "somedomain.com"
- }
-
-The ReRoute above will only be matched when the host header value is somedomain.com.
-
-If you do not set UpstreamHost on a ReRoute then any host header will match it. This means that if you have two ReRoutes that are the same, apart from the UpstreamHost, where one is null and the other set Ocelot will favour the one that has been set.
-
-This feature was requested as part of `Issue 216 `_ .
-
-Priority
-^^^^^^^^
-
-You can define the order you want your ReRoutes to match the Upstream HttpRequest by including a "Priority" property in ocelot.json
-See `Issue 270 `_ for reference
-
-.. code-block:: json
-
- {
- "Priority": 0
- }
-
-0 is the lowest priority, Ocelot will always use 0 for /{catchAll} ReRoutes and this is still hardcoded. After that you are free
-to set any priority you wish.
-
-e.g. you could have
-
-.. code-block:: json
-
- {
- "UpstreamPathTemplate": "/goods/{catchAll}"
- "Priority": 0
- }
-
-and
-
-.. code-block:: json
-
- {
- "UpstreamPathTemplate": "/goods/delete"
- "Priority": 1
- }
-
-In the example above if you make a request into Ocelot on /goods/delete Ocelot will match /goods/delete ReRoute. Previously it would have
-matched /goods/{catchAll} (because this is the first ReRoute in the list!).
-
-Dynamic Routing
-^^^^^^^^^^^^^^^
-
-This feature was requested in `issue 340 `_.
-
-The idea is to enable dynamic routing when using a service discovery provider so you don't have to provide the ReRoute config. See the docs :ref:`service-discovery` if
-this sounds interesting to you.
-
-Query Strings
-^^^^^^^^^^^^^
-
-Ocelot allows you to specify a query string as part of the DownstreamPathTemplate like the example below.
-
-.. code-block:: json
-
- {
- "ReRoutes": [
- {
- "DownstreamPathTemplate": "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}",
- "UpstreamPathTemplate": "/api/units/{subscriptionId}/{unitId}/updates",
- "UpstreamHttpMethod": [
- "Get"
- ],
- "DownstreamScheme": "http",
- "DownstreamHostAndPorts": [
- {
- "Host": "localhost",
- "Port": 50110
- }
- ]
- }
- ],
- "GlobalConfiguration": {
- }
- }
-
-In this example Ocelot will use the value from the {unitId} in the upstream path template and add it to the downstream request as a query string parameter called unitId!
-
-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
-
- {
- "ReRoutes": [
- {
- "DownstreamPathTemplate": "/api/units/{subscriptionId}/{unitId}/updates",
- "UpstreamPathTemplate": "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}",
- "UpstreamHttpMethod": [
- "Get"
- ],
- "DownstreamScheme": "http",
- "DownstreamHostAndPorts": [
- {
- "Host": "localhost",
- "Port": 50110
- }
- ]
- }
- ],
- "GlobalConfiguration": {
- }
- }
-
-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.
+Routing
+=======
+
+Ocelot's primary functionality is to take incoming http requests and forward them on to a downstream service. Ocelot currently only supports this in the form of another http request (in the future
+this could be any transport mechanism).
+
+Ocelot's describes the routing of one request to another as a Route. In order to get anything working in Ocelot you need to set up a Route in the configuration.
+
+.. code-block:: json
+
+ {
+ "Routes": [
+ ]
+ }
+
+To configure a Route you need to add one to the Routes json array.
+
+.. code-block:: json
+
+ {
+ "DownstreamPathTemplate": "/api/posts/{postId}",
+ "DownstreamScheme": "https",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "localhost",
+ "Port": 80,
+ }
+ ],
+ "UpstreamPathTemplate": "/posts/{postId}",
+ "UpstreamHttpMethod": [ "Put", "Delete" ]
+ }
+
+The DownstreamPathTemplate, DownstreamScheme and DownstreamHostAndPorts define the URL that a request will be forwarded to.
+
+DownstreamHostAndPorts is a collection that defines the host and port of any downstream services that you wish to forward requests to. Usually this will just contain a single entry but sometimes you might want to load balance requests to your downstream services and Ocelot allows you add more than one entry and then select a load balancer.
+
+The UpstreamPathTemplate is the URL that Ocelot will use to identify which DownstreamPathTemplate to use for a given request. The UpstreamHttpMethod is used so Ocelot can distinguish between requests with different HTTP verbs to the same URL. You can set a specific list of HTTP Methods or set an empty list to allow any of them.
+
+In Ocelot you can add placeholders for variables to your Templates in the form of {something}. The placeholder variable needs to be present in both the DownstreamPathTemplate and UpstreamPathTemplate properties. When it is Ocelot will attempt to substitute the value in the UpstreamPathTemplate placeholder into the DownstreamPathTemplate for each request Ocelot processes.
+
+You can also do a catch all type of Route e.g.
+
+.. code-block:: json
+
+ {
+ "DownstreamPathTemplate": "/api/{everything}",
+ "DownstreamScheme": "https",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "localhost",
+ "Port": 80,
+ }
+ ],
+ "UpstreamPathTemplate": "/{everything}",
+ "UpstreamHttpMethod": [ "Get", "Post" ]
+ }
+
+This will forward any path + query string combinations to the downstream service after the path /api.
+
+
+The default ReRouting configuration is case insensitive!
+
+In order to change this you can specify on a per Route basis the following setting.
+
+.. code-block:: json
+
+ "RouteIsCaseSensitive": true
+
+This means that when Ocelot tries to match the incoming upstream url with an upstream template the
+evaluation will be case sensitive.
+
+Catch All
+^^^^^^^^^
+
+Ocelot's routing also supports a catch all style routing where the user can specify that they want to match all traffic.
+
+If you set up your config like below, all requests will be proxied straight through. The placeholder {url} name is not significant, any name will work.
+
+.. code-block:: json
+
+ {
+ "DownstreamPathTemplate": "/{url}",
+ "DownstreamScheme": "https",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "localhost",
+ "Port": 80,
+ }
+ ],
+ "UpstreamPathTemplate": "/{url}",
+ "UpstreamHttpMethod": [ "Get" ]
+ }
+
+The catch all has a lower priority than any other Route. If you also have the Route below in your config then Ocelot would match it before the catch all.
+
+.. code-block:: json
+
+ {
+ "DownstreamPathTemplate": "/",
+ "DownstreamScheme": "https",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "10.0.10.1",
+ "Port": 80,
+ }
+ ],
+ "UpstreamPathTemplate": "/",
+ "UpstreamHttpMethod": [ "Get" ]
+ }
+
+Upstream Host
+^^^^^^^^^^^^^
+
+This feature allows you to have Routes based on the upstream host. This works by looking at the host header the client has used and then using this as part of the information we use to identify a Route.
+
+In order to use this feature please add the following to your config.
+
+.. code-block:: json
+
+ {
+ "DownstreamPathTemplate": "/",
+ "DownstreamScheme": "https",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "10.0.10.1",
+ "Port": 80,
+ }
+ ],
+ "UpstreamPathTemplate": "/",
+ "UpstreamHttpMethod": [ "Get" ],
+ "UpstreamHost": "somedomain.com"
+ }
+
+The Route above will only be matched when the host header value is somedomain.com.
+
+If you do not set UpstreamHost on a Route then any host header will match it. This means that if you have two Routes that are the same, apart from the UpstreamHost, where one is null and the other set Ocelot will favour the one that has been set.
+
+This feature was requested as part of `Issue 216 `_ .
+
+Priority
+^^^^^^^^
+
+You can define the order you want your Routes to match the Upstream HttpRequest by including a "Priority" property in ocelot.json
+See `Issue 270 `_ for reference
+
+.. code-block:: json
+
+ {
+ "Priority": 0
+ }
+
+0 is the lowest priority, Ocelot will always use 0 for /{catchAll} Routes and this is still hardcoded. After that you are free to set any priority you wish.
+
+e.g. you could have
+
+.. code-block:: json
+
+ {
+ "UpstreamPathTemplate": "/goods/{catchAll}"
+ "Priority": 0
+ }
+
+and
+
+.. code-block:: json
+
+ {
+ "UpstreamPathTemplate": "/goods/delete"
+ "Priority": 1
+ }
+
+In the example above if you make a request into Ocelot on /goods/delete Ocelot will match /goods/delete Route. Previously it would have matched /goods/{catchAll} (because this is the first Route in the list!).
+
+Dynamic Routing
+^^^^^^^^^^^^^^^
+
+This feature was requested in `issue 340 `_.
+
+The idea is to enable dynamic routing when using a service discovery provider so you don't have to provide the Route config. See the docs :ref:`service-discovery` if
+this sounds interesting to you.
+
+Query Strings
+^^^^^^^^^^^^^
+
+Ocelot allows you to specify a query string as part of the DownstreamPathTemplate like the example below.
+
+.. code-block:: json
+
+ {
+ "Routes": [
+ {
+ "DownstreamPathTemplate": "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}",
+ "UpstreamPathTemplate": "/api/units/{subscriptionId}/{unitId}/updates",
+ "UpstreamHttpMethod": [
+ "Get"
+ ],
+ "DownstreamScheme": "http",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "localhost",
+ "Port": 50110
+ }
+ ]
+ }
+ ],
+ "GlobalConfiguration": {
+ }
+ }
+
+In this example Ocelot will use the value from the {unitId} in the upstream path template and add it to the downstream request as a query string parameter called unitId!
+
+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
+
+ {
+ "Routes": [
+ {
+ "DownstreamPathTemplate": "/api/units/{subscriptionId}/{unitId}/updates",
+ "UpstreamPathTemplate": "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}",
+ "UpstreamHttpMethod": [
+ "Get"
+ ],
+ "DownstreamScheme": "http",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "localhost",
+ "Port": 50110
+ }
+ ]
+ }
+ ],
+ "GlobalConfiguration": {
+ }
+ }
+
+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.
diff --git a/docs/features/servicediscovery.rst b/docs/features/servicediscovery.rst
index f23d2a4a9..94b993cdf 100644
--- a/docs/features/servicediscovery.rst
+++ b/docs/features/servicediscovery.rst
@@ -1,252 +1,244 @@
-.. service-discovery:
-
-Service Discovery
-=================
-
-Ocelot allows you to specify a service discovery provider and will use this to find the host and port
-for the downstream service Ocelot is forwarding a request to. At the moment this is only supported in the
-GlobalConfiguration section which means the same service discovery provider will be used for all ReRoutes
-you specify a ServiceName for at ReRoute level.
-
-Consul
-^^^^^^
-
-The first thing you need to do is install the NuGet package that provides Consul support in Ocelot.
-
-``Install-Package Ocelot.Provider.Consul``
-
-Then add the following to your ConfigureServices method.
-
-.. code-block:: csharp
-
- s.AddOcelot()
- .AddConsul();
-
-The following is required in the GlobalConfiguration. The Provider is required and if you do not specify a host and port the Consul default
-will be used.
-
-.. code-block:: json
-
- "ServiceDiscoveryProvider": {
- "Host": "localhost",
- "Port": 8500,
- "Type": "Consul"
- }
-
-In the future we can add a feature that allows ReRoute specfic configuration.
-
-In order to tell Ocelot a ReRoute is to use the service discovery provider for its host and port you must add the
-ServiceName and load balancer you wish to use when making requests downstream. At the moment Ocelot has a RoundRobin
-and LeastConnection algorithm you can use. If no load balancer is specified Ocelot will not load balance requests.
-
-.. code-block:: json
-
- {
- "DownstreamPathTemplate": "/api/posts/{postId}",
- "DownstreamScheme": "https",
- "UpstreamPathTemplate": "/posts/{postId}",
- "UpstreamHttpMethod": [ "Put" ],
- "ServiceName": "product",
- "LoadBalancerOptions": {
- "Type": "LeastConnection"
- },
- }
-
-When this is set up Ocelot will lookup the downstream host and port from the service discover provider and load balance requests across any available services.
-
-A lot of people have asked me to implement a feature where Ocelot polls Consul for latest service information rather than per request. If you want to poll Consul for the latest services rather than per request (default behaviour) then you need to set the following configuration.
-
-.. code-block:: json
-
- "ServiceDiscoveryProvider": {
- "Host": "localhost",
- "Port": 8500,
- "Type": "PollConsul",
- "PollingInterval": 100
- }
-
-The polling interval is in milliseconds and tells Ocelot how often to call Consul for changes in service configuration.
-
-Please note there are tradeoffs here. If you poll Consul it is possible Ocelot will not know if a service is down depending on your polling interval and you might get more errors than if you get the latest services per request. This really depends on how volatile your services are. I doubt it will matter for most people and polling may give a tiny performance improvement over calling Consul per request (as sidecar agent). If you are calling a remote Consul agent then polling will be a good performance improvement.
-
-Your services need to be added to Consul something like below (C# style but hopefully this make sense)...The only important thing to note
-is not to add http or https to the Address field. I have been contacted before about not accepting scheme in Address and accepting scheme
-in address. After reading `this `_ I don't think the scheme should be in there.
-
-.. code-block: csharp
-
- new AgentService()
- {
- Service = "some-service-name",
- Address = "localhost",
- Port = 8080,
- ID = "some-id",
- }
-
-Or
-
-.. code-block:: json
-
- "Service": {
- "ID": "some-id",
- "Service": "some-service-name",
- "Address": "localhost",
- "Port": 8080
- }
-
-ACL Token
----------
-
-If you are using ACL with Consul Ocelot supports adding the X-Consul-Token header. In order so this to work you must add the additional property below.
-
-.. code-block:: json
-
- "ServiceDiscoveryProvider": {
- "Host": "localhost",
- "Port": 8500,
- "Token": "footoken",
- "Type": "Consul"
- }
-
-Ocelot will add this token to the Consul client that it uses to make requests and that is then used for every request.
-
-Eureka
-^^^^^^
-
-This feature was requested as part of `Issue 262 `_ . to add support for Netflix's
-Eureka service discovery provider. The main reason for this is it is a key part of `Steeltoe `_ which is something
-to do with `Pivotal `_! Anyway enough of the background.
-
-The first thing you need to do is install the NuGet package that provides Eureka support in Ocelot.
-
-``Install-Package Ocelot.Provider.Eureka``
-
-Then add the following to your ConfigureServices method.
-
-.. code-block:: csharp
-
- s.AddOcelot()
- .AddEureka();
-
-Then in order to get this working add the following to ocelot.json..
-
-.. code-block:: json
-
- "ServiceDiscoveryProvider": {
- "Type": "Eureka"
- }
-
-And following the guide `Here `_ you may also need to add some stuff to appsettings.json. For example the json below tells the steeltoe / pivotal services where to look for the service discovery server and if the service should register with it.
-
-.. code-block:: json
-
- "eureka": {
- "client": {
- "serviceUrl": "http://localhost:8761/eureka/",
- "shouldRegisterWithEureka": false,
- "shouldFetchRegistry": true
- }
- }
-
-I am told that if shouldRegisterWithEureka is false then shouldFetchRegistry will defaut to true so you don't need it explicitly but left it in there.
-
-Ocelot will now register all the necessary services when it starts up and if you have the json above will register itself with
-Eureka. One of the services polls Eureka every 30 seconds (default) and gets the latest service state and persists this in memory.
-When Ocelot asks for a given service it is retrieved from memory so performance is not a big problem. Please note that this code
-is provided by the Pivotal.Discovery.Client NuGet package so big thanks to them for all the hard work.
-
-Ocelot will use the scheme (http/https) set in Eureka if these values are not provided in ocelot.json
-
-Dynamic Routing
-^^^^^^^^^^^^^^^
-
-This feature was requested in `issue 340 `_. The idea is to enable dynamic routing when using a service discovery provider (see that section of the docs for more info). In this mode Ocelot will use the first segment of the upstream path to lookup the downstream service with the service discovery provider.
-
-An example of this would be calling Ocelot with a url like https://api.mywebsite.com/product/products. Ocelot will take the first segment of
-the path which is product and use it as a key to look up the service in Consul. If Consul returns a service Ocelot will request it on whatever host and port comes back from Consul plus the remaining path segments in this case products thus making the downstream call http://hostfromconsul:portfromconsul/products. Ocelot will apprend any query string to the downstream url as normal.
-
-In order to enable dynamic routing you need to have 0 ReRoutes in your config. At the moment you cannot mix dynamic and configuration ReRoutes. In addition to this you need to specify the Service Discovery provider details as outlined above and the downstream http/https scheme as DownstreamScheme.
-
-In addition to that you can set RateLimitOptions, QoSOptions, LoadBalancerOptions and HttpHandlerOptions, DownstreamScheme (You might want to call Ocelot on https but talk to private services over http) that will be applied to all of the dynamic ReRoutes.
-
-The config might look something like
-
-.. code-block:: json
-
- {
- "ReRoutes": [],
- "Aggregates": [],
- "GlobalConfiguration": {
- "RequestIdKey": null,
- "ServiceDiscoveryProvider": {
- "Host": "localhost",
- "Port": 8500,
- "Type": "Consul",
- "Token": null,
- "ConfigurationKey": null
- },
- "RateLimitOptions": {
- "ClientIdHeader": "ClientId",
- "QuotaExceededMessage": null,
- "RateLimitCounterPrefix": "ocelot",
- "DisableRateLimitHeaders": false,
- "HttpStatusCode": 429
- },
- "QoSOptions": {
- "ExceptionsAllowedBeforeBreaking": 0,
- "DurationOfBreak": 0,
- "TimeoutValue": 0
- },
- "BaseUrl": null,
- "LoadBalancerOptions": {
- "Type": "LeastConnection",
- "Key": null,
- "Expiry": 0
- },
- "DownstreamScheme": "http",
- "HttpHandlerOptions": {
- "AllowAutoRedirect": false,
- "UseCookieContainer": false,
- "UseTracing": false
- }
- }
- }
-
-Ocelot also allows you to set DynamicReRoutes which lets you set rate limiting rules per downstream service. This is useful if you have for example a product and search service and you want to rate limit one more than the other. An example of this would be as follows.
-
-.. code-block:: json
-
- {
- "DynamicReRoutes": [
- {
- "ServiceName": "product",
- "RateLimitRule": {
- "ClientWhitelist": [],
- "EnableRateLimiting": true,
- "Period": "1s",
- "PeriodTimespan": 1000.0,
- "Limit": 3
- }
- }
- ],
- "GlobalConfiguration": {
- "RequestIdKey": null,
- "ServiceDiscoveryProvider": {
- "Host": "localhost",
- "Port": 8523,
- "Type": "Consul"
- },
- "RateLimitOptions": {
- "ClientIdHeader": "ClientId",
- "QuotaExceededMessage": "",
- "RateLimitCounterPrefix": "",
- "DisableRateLimitHeaders": false,
- "HttpStatusCode": 428
- }
- "DownstreamScheme": "http",
- }
- }
-
-This configuration means that if you have a request come into Ocelot on /product/* then dynamic routing will kick in and ocelot will use the rate limiting set against the product service in the DynamicReRoutes section.
-
-Please take a look through all of the docs to understand these options.
+.. service-discovery:
+
+Service Discovery
+=================
+
+Ocelot allows you to specify a service discovery provider and will use this to find the host and port for the downstream service Ocelot is forwarding a request to. At the moment this is only supported in the
+GlobalConfiguration section which means the same service discovery provider will be used for all Routes you specify a ServiceName for at Route level.
+
+Consul
+^^^^^^
+
+The first thing you need to do is install the NuGet package that provides Consul support in Ocelot.
+
+``Install-Package Ocelot.Provider.Consul``
+
+Then add the following to your ConfigureServices method.
+
+.. code-block:: csharp
+
+ s.AddOcelot()
+ .AddConsul();
+
+The following is required in the GlobalConfiguration. The Provider is required and if you do not specify a host and port the Consul default
+will be used.
+
+Please note the Scheme option defauls to HTTP. It was added in this `PR `_. It defaults to HTTP to not introduce a breaking change.
+
+.. code-block:: json
+
+ "ServiceDiscoveryProvider": {
+ "Scheme": "https",
+ "Host": "localhost",
+ "Port": 8500,
+ "Type": "Consul"
+ }
+
+In the future we can add a feature that allows Route specfic configuration.
+
+In order to tell Ocelot a Route is to use the service discovery provider for its host and port you must add the ServiceName and load balancer you wish to use when making requests downstream. At the moment Ocelot has a RoundRobin and LeastConnection algorithm you can use. If no load balancer is specified Ocelot will not load balance requests.
+
+.. code-block:: json
+
+ {
+ "DownstreamPathTemplate": "/api/posts/{postId}",
+ "DownstreamScheme": "https",
+ "UpstreamPathTemplate": "/posts/{postId}",
+ "UpstreamHttpMethod": [ "Put" ],
+ "ServiceName": "product",
+ "LoadBalancerOptions": {
+ "Type": "LeastConnection"
+ },
+ }
+
+When this is set up Ocelot will lookup the downstream host and port from the service discover provider and load balance requests across any available services.
+
+A lot of people have asked me to implement a feature where Ocelot polls Consul for latest service information rather than per request. If you want to poll Consul for the latest services rather than per request (default behaviour) then you need to set the following configuration.
+
+.. code-block:: json
+
+ "ServiceDiscoveryProvider": {
+ "Host": "localhost",
+ "Port": 8500,
+ "Type": "PollConsul",
+ "PollingInterval": 100
+ }
+
+The polling interval is in milliseconds and tells Ocelot how often to call Consul for changes in service configuration.
+
+Please note there are tradeoffs here. If you poll Consul it is possible Ocelot will not know if a service is down depending on your polling interval and you might get more errors than if you get the latest services per request. This really depends on how volatile your services are. I doubt it will matter for most people and polling may give a tiny performance improvement over calling Consul per request (as sidecar agent). If you are calling a remote Consul agent then polling will be a good performance improvement.
+
+Your services need to be added to Consul something like below (C# style but hopefully this make sense)...The only important thing to note is not to add http or https to the Address field. I have been contacted before about not accepting scheme in Address and accepting scheme in address. After reading `this `_ I don't think the scheme should be in there.
+
+.. code-block: csharp
+
+ new AgentService()
+ {
+ Service = "some-service-name",
+ Address = "localhost",
+ Port = 8080,
+ ID = "some-id",
+ }
+
+Or
+
+.. code-block:: json
+
+ "Service": {
+ "ID": "some-id",
+ "Service": "some-service-name",
+ "Address": "localhost",
+ "Port": 8080
+ }
+
+ACL Token
+---------
+
+If you are using ACL with Consul Ocelot supports adding the X-Consul-Token header. In order so this to work you must add the additional property below.
+
+.. code-block:: json
+
+ "ServiceDiscoveryProvider": {
+ "Host": "localhost",
+ "Port": 8500,
+ "Token": "footoken",
+ "Type": "Consul"
+ }
+
+Ocelot will add this token to the Consul client that it uses to make requests and that is then used for every request.
+
+Eureka
+^^^^^^
+
+This feature was requested as part of `Issue 262 `_ . to add support for Netflix's Eureka service discovery provider. The main reason for this is it is a key part of `Steeltoe `_ which is something to do with `Pivotal `_! Anyway enough of the background.
+
+The first thing you need to do is install the NuGet package that provides Eureka support in Ocelot.
+
+``Install-Package Ocelot.Provider.Eureka``
+
+Then add the following to your ConfigureServices method.
+
+.. code-block:: csharp
+
+ s.AddOcelot()
+ .AddEureka();
+
+Then in order to get this working add the following to ocelot.json..
+
+.. code-block:: json
+
+ "ServiceDiscoveryProvider": {
+ "Type": "Eureka"
+ }
+
+And following the guide `Here `_ you may also need to add some stuff to appsettings.json. For example the json below tells the steeltoe / pivotal services where to look for the service discovery server and if the service should register with it.
+
+.. code-block:: json
+
+ "eureka": {
+ "client": {
+ "serviceUrl": "http://localhost:8761/eureka/",
+ "shouldRegisterWithEureka": false,
+ "shouldFetchRegistry": true
+ }
+ }
+
+I am told that if shouldRegisterWithEureka is false then shouldFetchRegistry will defaut to true so you don't need it explicitly but left it in there.
+
+Ocelot will now register all the necessary services when it starts up and if you have the json above will register itself with Eureka. One of the services polls Eureka every 30 seconds (default) and gets the latest service state and persists this in memory. When Ocelot asks for a given service it is retrieved from memory so performance is not a big problem. Please note that this code is provided by the Pivotal.Discovery.Client NuGet package so big thanks to them for all the hard work.
+
+Ocelot will use the scheme (http/https) set in Eureka if these values are not provided in ocelot.json
+
+Dynamic Routing
+^^^^^^^^^^^^^^^
+
+This feature was requested in `issue 340 `_. The idea is to enable dynamic routing when using a service discovery provider (see that section of the docs for more info). In this mode Ocelot will use the first segment of the upstream path to lookup the downstream service with the service discovery provider.
+
+An example of this would be calling Ocelot with a url like https://api.mywebsite.com/product/products. Ocelot will take the first segment of
+the path which is product and use it as a key to look up the service in Consul. If Consul returns a service Ocelot will request it on whatever host and port comes back from Consul plus the remaining path segments in this case products thus making the downstream call http://hostfromconsul:portfromconsul/products. Ocelot will apprend any query string to the downstream url as normal.
+
+In order to enable dynamic routing you need to have 0 Routes in your config. At the moment you cannot mix dynamic and configuration Routes. In addition to this you need to specify the Service Discovery provider details as outlined above and the downstream http/https scheme as DownstreamScheme.
+
+In addition to that you can set RateLimitOptions, QoSOptions, LoadBalancerOptions and HttpHandlerOptions, DownstreamScheme (You might want to call Ocelot on https but talk to private services over http) that will be applied to all of the dynamic Routes.
+
+The config might look something like
+
+.. code-block:: json
+
+ {
+ "Routes": [],
+ "Aggregates": [],
+ "GlobalConfiguration": {
+ "RequestIdKey": null,
+ "ServiceDiscoveryProvider": {
+ "Host": "localhost",
+ "Port": 8500,
+ "Type": "Consul",
+ "Token": null,
+ "ConfigurationKey": null
+ },
+ "RateLimitOptions": {
+ "ClientIdHeader": "ClientId",
+ "QuotaExceededMessage": null,
+ "RateLimitCounterPrefix": "ocelot",
+ "DisableRateLimitHeaders": false,
+ "HttpStatusCode": 429
+ },
+ "QoSOptions": {
+ "ExceptionsAllowedBeforeBreaking": 0,
+ "DurationOfBreak": 0,
+ "TimeoutValue": 0
+ },
+ "BaseUrl": null,
+ "LoadBalancerOptions": {
+ "Type": "LeastConnection",
+ "Key": null,
+ "Expiry": 0
+ },
+ "DownstreamScheme": "http",
+ "HttpHandlerOptions": {
+ "AllowAutoRedirect": false,
+ "UseCookieContainer": false,
+ "UseTracing": false
+ }
+ }
+ }
+
+Ocelot also allows you to set DynamicRoutes which lets you set rate limiting rules per downstream service. This is useful if you have for example a product and search service and you want to rate limit one more than the other. An example of this would be as follows.
+
+.. code-block:: json
+
+ {
+ "DynamicRoutes": [
+ {
+ "ServiceName": "product",
+ "RateLimitRule": {
+ "ClientWhitelist": [],
+ "EnableRateLimiting": true,
+ "Period": "1s",
+ "PeriodTimespan": 1000.0,
+ "Limit": 3
+ }
+ }
+ ],
+ "GlobalConfiguration": {
+ "RequestIdKey": null,
+ "ServiceDiscoveryProvider": {
+ "Host": "localhost",
+ "Port": 8523,
+ "Type": "Consul"
+ },
+ "RateLimitOptions": {
+ "ClientIdHeader": "ClientId",
+ "QuotaExceededMessage": "",
+ "RateLimitCounterPrefix": "",
+ "DisableRateLimitHeaders": false,
+ "HttpStatusCode": 428
+ }
+ "DownstreamScheme": "http",
+ }
+ }
+
+This configuration means that if you have a request come into Ocelot on /product/* then dynamic routing will kick in and ocelot will use the rate limiting set against the product service in the DynamicRoutes section.
+
+Please take a look through all of the docs to understand these options.
diff --git a/docs/features/servicefabric.rst b/docs/features/servicefabric.rst
index 0bc3ee1b5..64e62d191 100644
--- a/docs/features/servicefabric.rst
+++ b/docs/features/servicefabric.rst
@@ -1,42 +1,40 @@
-Service Fabric
-==============
-
-If you have services deployed in Service Fabric you will normally use the naming service to access them.
-
-The following example shows how to set up a ReRoute that will work in Service Fabric. The most important thing is the ServiceName which is made up of the
-Service Fabric application name then the specific service name. We also need to set up the ServiceDiscoveryProvider in
-GlobalConfiguration. The example here shows a typical configuration. It assumes service fabric is running on localhost and that the naming service is on port 19081.
-
-The example below is taken from the samples folder so please check it if this doesnt make sense!
-
-.. code-block:: json
-
- {
- "ReRoutes": [
- {
- "DownstreamPathTemplate": "/api/values",
- "UpstreamPathTemplate": "/EquipmentInterfaces",
- "UpstreamHttpMethod": [
- "Get"
- ],
- "DownstreamScheme": "http",
- "ServiceName": "OcelotServiceApplication/OcelotApplicationService",
- }
- ],
- "GlobalConfiguration": {
- "RequestIdKey": "OcRequestId",
- "ServiceDiscoveryProvider": {
- "Host": "localhost",
- "Port": 19081,
- "Type": "ServiceFabric"
- }
- }
- }
-
-If you are using stateless / guest exe services ocelot will be able to proxy through the naming service without anything else. However
-if you are using statefull / actor services you must send the PartitionKind and PartitionKey query string values with the client
-request e.g.
-
-GET http://ocelot.com/EquipmentInterfaces?PartitionKind=xxx&PartitionKey=xxx
-
-There is no way for Ocelot to work these out for you.
+Service Fabric
+==============
+
+If you have services deployed in Service Fabric you will normally use the naming service to access them.
+
+The following example shows how to set up a Route that will work in Service Fabric. The most important thing is the ServiceName which is made up of the Service Fabric application name then the specific service name. We also need to set up the ServiceDiscoveryProvider in GlobalConfiguration. The example here shows a typical configuration. It assumes service fabric is running on localhost and that the naming service is on port 19081.
+
+The example below is taken from the samples folder so please check it if this doesnt make sense!
+
+.. code-block:: json
+
+ {
+ "Routes": [
+ {
+ "DownstreamPathTemplate": "/api/values",
+ "UpstreamPathTemplate": "/EquipmentInterfaces",
+ "UpstreamHttpMethod": [
+ "Get"
+ ],
+ "DownstreamScheme": "http",
+ "ServiceName": "OcelotServiceApplication/OcelotApplicationService",
+ }
+ ],
+ "GlobalConfiguration": {
+ "RequestIdKey": "OcRequestId",
+ "ServiceDiscoveryProvider": {
+ "Host": "localhost",
+ "Port": 19081,
+ "Type": "ServiceFabric"
+ }
+ }
+ }
+
+If you are using stateless / guest exe services ocelot will be able to proxy through the naming service without anything else. However
+if you are using statefull / actor services you must send the PartitionKind and PartitionKey query string values with the client
+request e.g.
+
+GET http://ocelot.com/EquipmentInterfaces?PartitionKind=xxx&PartitionKey=xxx
+
+There is no way for Ocelot to work these out for you.
diff --git a/docs/features/tracing.rst b/docs/features/tracing.rst
index bda436275..a400a8091 100644
--- a/docs/features/tracing.rst
+++ b/docs/features/tracing.rst
@@ -1,41 +1,74 @@
-Tracing
-=======
-
-This page details how to perform distributed tracing with Ocelot. At the moment we only support Butterfly but other tracers might just work without
-anything Ocelot specific.
-
-Butterfly
-^^^^^^^^^
-
-Ocelot providers tracing functionality from the excellent `Butterfly `_ project. The code for the Ocelot integration
-can be found `here `_.
-
-In order to use the tracing please read the Butterfly documentation.
-
-In ocelot you need to do the following if you wish to trace a ReRoute.
-
- ``Install-Package Ocelot.Tracing.Butterfly``
-
-In your ConfigureServices method
-
-.. code-block:: csharp
-
- services
- .AddOcelot()
- // this comes from Ocelot.Tracing.Butterfly package
- .AddButterfly(option =>
- {
- //this is the url that the butterfly collector server is running on...
- option.CollectorUrl = "http://localhost:9618";
- option.Service = "Ocelot";
- });
-
-Then in your ocelot.json add the following to the ReRoute you want to trace..
-
-.. code-block:: json
-
- "HttpHandlerOptions": {
- "UseTracing": true
- },
-
-Ocelot will now send tracing information to Butterfly when this ReRoute is called.
+Tracing
+=======
+
+This page details how to perform distributed tracing with Ocelot.
+
+OpenTracing
+^^^^^^^^^^^
+
+Ocelot providers tracing functionality from the excellent `OpenTracing C# `_ project. The code for the Ocelot integration
+can be found `here `_.
+
+The example below uses `Jaeger C# `_ client to provide the tracer used in Ocelot.
+
+.. code-block:: csharp
+
+ services.AddSingleton(sp =>
+ {
+ var loggerFactory = sp.GetService();
+ Configuration config = new Configuration(context.HostingEnvironment.ApplicationName, loggerFactory);
+
+ var tracer = config.GetTracer();
+ GlobalTracer.Register(tracer);
+ return tracer;
+ });
+
+ services
+ .AddOcelot()
+ .AddOpenTracing();
+
+Then in your ocelot.json add the following to the Route you want to trace..
+
+.. code-block:: json
+
+ "HttpHandlerOptions": {
+ "UseTracing": true
+ },
+
+Ocelot will now send tracing information to Jaeger when this Route is called.
+
+Butterfly
+^^^^^^^^^
+
+Ocelot providers tracing functionality from the excellent `Butterfly `_ project. The code for the Ocelot integration
+can be found `here `_.
+
+In order to use the tracing please read the Butterfly documentation.
+
+In ocelot you need to do the following if you wish to trace a Route.
+
+ ``Install-Package Ocelot.Tracing.Butterfly``
+
+In your ConfigureServices method
+
+.. code-block:: csharp
+
+ services
+ .AddOcelot()
+ // this comes from Ocelot.Tracing.Butterfly package
+ .AddButterfly(option =>
+ {
+ //this is the url that the butterfly collector server is running on...
+ option.CollectorUrl = "http://localhost:9618";
+ option.Service = "Ocelot";
+ });
+
+Then in your ocelot.json add the following to the Route you want to trace..
+
+.. code-block:: json
+
+ "HttpHandlerOptions": {
+ "UseTracing": true
+ },
+
+Ocelot will now send tracing information to Butterfly when this Route is called.
diff --git a/docs/features/websockets.rst b/docs/features/websockets.rst
index 5b5879193..ad185f297 100644
--- a/docs/features/websockets.rst
+++ b/docs/features/websockets.rst
@@ -1,114 +1,109 @@
-Websockets
-==========
-
-Ocelot supports proxying websockets with some extra bits. This functionality was requested in `Issue 212 `_.
-
-In order to get websocket proxying working with Ocelot you need to do the following.
-
-In your Configure method you need to tell your application to use WebSockets.
-
-.. code-block:: csharp
-
- Configure(app =>
- {
- app.UseWebSockets();
- app.UseOcelot().Wait();
- })
-
-Then in your ocelot.json add the following to proxy a ReRoute using websockets.
-
-.. code-block:: json
-
- {
- "DownstreamPathTemplate": "/ws",
- "UpstreamPathTemplate": "/",
- "DownstreamScheme": "ws",
- "DownstreamHostAndPorts": [
- {
- "Host": "localhost",
- "Port": 5001
- }
- ],
- }
-
-With this configuration set Ocelot will match any websocket traffic that comes in on / and proxy it to localhost:5001/ws. To make this clearer
-Ocelot will receive messages from the upstream client, proxy these to the downstream service, receive messages from the downstream service and
-proxy these to the upstream client.
-
-SignalR
-^^^^^^^
-
-Ocelot supports proxying SignalR. This functionality was requested in `Issue 344 `_.
-
-In order to get websocket proxying working with Ocelot you need to do the following.
-
-Install Microsoft.AspNetCore.SignalR.Client 1.0.2 you can try other packages but this one is tested.
-
-Do not run it in IISExpress or install the websockets feature in the IIS features
-
-In your Configure method you need to tell your application to use SignalR.
-
-.. code-block:: csharp
-
- Configure(app =>
- {
- app.UseWebSockets();
- app.UseOcelot().Wait();
- })
-
-Then in your ocelot.json add the following to proxy a ReRoute using SignalR. Note normal Ocelot routing rules apply the main thing is the scheme which is set to "ws".
-
-.. code-block:: json
-
- {
- "ReRoutes": [
- {
- "DownstreamPathTemplate": "/{catchAll}",
- "DownstreamScheme": "ws",
- "DownstreamHostAndPorts": [
- {
- "Host": "localhost",
- "Port": 50000
- }
- ],
- "UpstreamPathTemplate": "/gateway/{catchAll}",
- "UpstreamHttpMethod": [ "GET", "POST", "PUT", "DELETE", "OPTIONS" ]
- }
- ]
-}
-
-With this configuration set Ocelot will match any SignalR traffic that comes in on / and proxy it to localhost:5001/ws. To make this clearer
-Ocelot will receive messages from the upstream client, proxy these to the downstream service, receive messages from the downstream service and
-proxy these to the upstream client.
-
-Supported
-^^^^^^^^^
-
-1. Load Balancer
-2. Routing
-3. Service Discovery
-
-This means that you can set up your downstream services running websockets and either have multiple DownstreamHostAndPorts in your ReRoute
-config or hook your ReRoute into a service discovery provider and then load balance requests...Which I think is pretty cool :)
-
-Not Supported
-^^^^^^^^^^^^^
-
-Unfortunately a lot of Ocelot's features are non websocket specific such as header and http client stuff. I've listed what won't work below.
-
-1. Tracing
-2. RequestId
-3. Request Aggregation
-4. Rate Limiting
-5. Quality of Service
-6. Middleware Injection
-7. Header Transformation
-8. Delegating Handlers
-9. Claims Transformation
-10. Caching
-11. Authentication - If anyone requests it we might be able to do something with basic authentication.
-12. Authorisation
-
-I'm not 100% sure what will happen with this feature when it get's into the wild so please make sure you test thoroughly!
-
-
+Websockets
+==========
+
+Ocelot supports proxying websockets with some extra bits. This functionality was requested in `Issue 212 `_.
+
+In order to get websocket proxying working with Ocelot you need to do the following.
+
+In your Configure method you need to tell your application to use WebSockets.
+
+.. code-block:: csharp
+
+ Configure(app =>
+ {
+ app.UseWebSockets();
+ app.UseOcelot().Wait();
+ })
+
+Then in your ocelot.json add the following to proxy a Route using websockets.
+
+.. code-block:: json
+
+ {
+ "DownstreamPathTemplate": "/ws",
+ "UpstreamPathTemplate": "/",
+ "DownstreamScheme": "ws",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "localhost",
+ "Port": 5001
+ }
+ ],
+ }
+
+With this configuration set Ocelot will match any websocket traffic that comes in on / and proxy it to localhost:5001/ws. To make this clearer Ocelot will receive messages from the upstream client, proxy these to the downstream service, receive messages from the downstream service and proxy these to the upstream client.
+
+SignalR
+^^^^^^^
+
+Ocelot supports proxying SignalR. This functionality was requested in `Issue 344 `_.
+
+In order to get websocket proxying working with Ocelot you need to do the following.
+
+Install Microsoft.AspNetCore.SignalR.Client 1.0.2 you can try other packages but this one is tested.
+
+Do not run it in IISExpress or install the websockets feature in the IIS features
+
+In your Configure method you need to tell your application to use SignalR.
+
+.. code-block:: csharp
+
+ Configure(app =>
+ {
+ app.UseWebSockets();
+ app.UseOcelot().Wait();
+ })
+
+Then in your ocelot.json add the following to proxy a Route using SignalR. Note normal Ocelot routing rules apply the main thing is the scheme which is set to "ws".
+
+.. code-block:: json
+
+ {
+ "Routes": [
+ {
+ "DownstreamPathTemplate": "/{catchAll}",
+ "DownstreamScheme": "ws",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "localhost",
+ "Port": 50000
+ }
+ ],
+ "UpstreamPathTemplate": "/gateway/{catchAll}",
+ "UpstreamHttpMethod": [ "GET", "POST", "PUT", "DELETE", "OPTIONS" ]
+ }
+ ]
+}
+
+With this configuration set Ocelot will match any SignalR traffic that comes in on / and proxy it to localhost:5001/ws. To make this clearer Ocelot will receive messages from the upstream client, proxy these to the downstream service, receive messages from the downstream service and proxy these to the upstream client.
+
+Supported
+^^^^^^^^^
+
+1. Load Balancer
+2. Routing
+3. Service Discovery
+
+This means that you can set up your downstream services running websockets and either have multiple DownstreamHostAndPorts in your Route config or hook your Route into a service discovery provider and then load balance requests...Which I think is pretty cool :)
+
+Not Supported
+^^^^^^^^^^^^^
+
+Unfortunately a lot of Ocelot's features are non websocket specific such as header and http client stuff. I've listed what won't work below.
+
+1. Tracing
+2. RequestId
+3. Request Aggregation
+4. Rate Limiting
+5. Quality of Service
+6. Middleware Injection
+7. Header Transformation
+8. Delegating Handlers
+9. Claims Transformation
+10. Caching
+11. Authentication - If anyone requests it we might be able to do something with basic authentication.
+12. Authorization
+
+I'm not 100% sure what will happen with this feature when it get's into the wild so please make sure you test thoroughly!
+
+
diff --git a/docs/index.rst b/docs/index.rst
index 4aba33fd9..8df5b21c4 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -12,7 +12,8 @@ Thanks for taking a look at the Ocelot documentation. Please use the left hand n
introduction/gettingstarted
introduction/contributing
introduction/notsupported
-
+ introduction/gotchas
+
.. toctree::
:maxdepth: 2
:hidden:
@@ -23,16 +24,17 @@ Thanks for taking a look at the Ocelot documentation. Please use the left hand n
features/requestaggregation
features/graphql
features/servicediscovery
- features/servicefabric
+ features/servicefabric
features/kubernetes
features/authentication
- features/authorisation
+ features/authorization
features/websockets
features/administration
features/ratelimiting
features/caching
features/qualityofservice
features/headerstransformation
+ features/methodtransformation
features/claimstransformation
features/logging
features/tracing
@@ -40,7 +42,7 @@ Thanks for taking a look at the Ocelot documentation. Please use the left hand n
features/middlewareinjection
features/loadbalancer
features/delegatinghandlers
- features/raft
+ features/errorcodes
.. toctree::
:maxdepth: 2
diff --git a/docs/introduction/bigpicture.rst b/docs/introduction/bigpicture.rst
index 0ea39e172..ee56f0efa 100644
--- a/docs/introduction/bigpicture.rst
+++ b/docs/introduction/bigpicture.rst
@@ -1,23 +1,13 @@
Big Picture
===========
-Ocelot is aimed at people using .NET running
-a micro services / service orientated architecture
-that need a unified point of entry into their system.
+Ocelot is aimed at people using .NET running a micro services / service orientated architecture that need a unified point of entry into their system.
-In particular I want easy integration with
-IdentityServer reference and bearer tokens.
+In particular I want easy integration with IdentityServer reference and bearer tokens.
Ocelot is a bunch of middlewares in a specific order.
-Ocelot manipulates the HttpRequest object into a state specified by its configuration until
-it reaches a request builder middleware where it creates a HttpRequestMessage object which is
-used to make a request to a downstream service. The middleware that makes the request is
-the last thing in the Ocelot pipeline. It does not call the next middleware.
-The response from the downstream service is stored in a per request scoped repository
-and retrieved as the requests goes back up the Ocelot pipeline. There is a piece of middleware
-that maps the HttpResponseMessage onto the HttpResponse object and that is returned to the client.
-That is basically it with a bunch of other features.
+Ocelot manipulates the HttpRequest object into a state specified by its configuration until it reaches a request builder middleware where it creates a HttpRequestMessage object which is used to make a request to a downstream service. The middleware that makes the request is the last thing in the Ocelot pipeline. It does not call the next middleware. There is a piece of middleware that maps the HttpResponseMessage onto the HttpResponse object and that is returned to the client. That is basically it with a bunch of other features.
The following are configurations that you use when deploying Ocelot.
diff --git a/docs/introduction/contributing.rst b/docs/introduction/contributing.rst
index 535e22275..db05d5d92 100644
--- a/docs/introduction/contributing.rst
+++ b/docs/introduction/contributing.rst
@@ -1,5 +1,4 @@
Contributing
============
-Pull requests, issues and commentary welcome! No special process just create a request and get in
-touch either via gitter or create an issue.
\ No newline at end of file
+Pull requests, issues and commentary welcome! No special process just create a request and get in touch either via gitter or create an issue.
\ No newline at end of file
diff --git a/docs/introduction/gettingstarted.rst b/docs/introduction/gettingstarted.rst
index e2178c984..e29983b33 100644
--- a/docs/introduction/gettingstarted.rst
+++ b/docs/introduction/gettingstarted.rst
@@ -1,15 +1,14 @@
Getting Started
===============
-Ocelot is designed to work with .NET Core only and is currently
-built to netstandard2.0. `This `_ documentation may prove helpful when working out if Ocelot would be suitable for you.
+Ocelot is designed to work with ASP.NET and is currently on net6.0.
-.NET Core 3.1
-^^^^^^^^^^^^^
+.NET 7.0
+^^^^^^^^
**Install NuGet package**
-Install Ocelot and it's dependencies using nuget. You will need to create a netstandard2.0 project and bring the package into it. Then follow the Startup below and :doc:`../features/configuration` sections
+Install Ocelot and it's dependencies using nuget. You will need to create a net6.0 project and bring the package into it. Then follow the Startup below and :doc:`../features/configuration` sections
to get up and running.
``Install-Package Ocelot``
@@ -23,7 +22,7 @@ The following is a very basic ocelot.json. It won't do anything but should get O
.. code-block:: json
{
- "ReRoutes": [],
+ "Routes": [],
"GlobalConfiguration": {
"BaseUrl": "https://api.mybusiness.com"
}
@@ -34,7 +33,7 @@ If you want some example that actually does something use the following:
.. code-block:: json
{
- "ReRoutes": [
+ "Routes": [
{
"DownstreamPathTemplate": "/todos/{id}",
"DownstreamScheme": "https",
@@ -53,16 +52,13 @@ If you want some example that actually does something use the following:
}
}
-The most important thing to note here is BaseUrl. Ocelot needs to know the URL it is running under
-in order to do Header find & replace and for certain administration configurations. When setting this URL it should be the external URL that clients will see Ocelot running on e.g. If you are running containers Ocelot might run on the url http://123.12.1.1:6543 but has something like nginx in front of it responding on https://api.mybusiness.com. In this case the Ocelot base url should be https://api.mybusiness.com.
+The most important thing to note here is BaseUrl. Ocelot needs to know the URL it is running under in order to do Header find & replace and for certain administration configurations. When setting this URL it should be the external URL that clients will see Ocelot running on e.g. If you are running containers Ocelot might run on the url http://123.12.1.1:6543 but has something like nginx in front of it responding on https://api.mybusiness.com. In this case the Ocelot base url should be https://api.mybusiness.com.
-If you are using containers and require Ocelot to respond to clients on http://123.12.1.1:6543
-then you can do this, however if you are deploying multiple Ocelot's you will probably want to pass this on the command line in some kind of script. Hopefully whatever scheduler you are using can pass the IP.
+If you are using containers and require Ocelot to respond to clients on http://123.12.1.1:6543 then you can do this, however if you are deploying multiple Ocelot's you will probably want to pass this on the command line in some kind of script. Hopefully whatever scheduler you are using can pass the IP.
**Program**
-Then in your Program.cs you will want to have the following. The main things to note are
-AddOcelot() (adds ocelot services), UseOcelot().Wait() (sets up all the Ocelot middleware).
+Then in your Program.cs you will want to have the following. The main things to note are AddOcelot() (adds ocelot services), UseOcelot().Wait() (sets up all the Ocelot middleware).
.. code-block:: csharp
@@ -107,88 +103,4 @@ AddOcelot() (adds ocelot services), UseOcelot().Wait() (sets up all the Ocelot m
.Run();
}
}
- }
-
-
- **Note:** When using ASP.NET Core 2.2 and you want to use In-Process hosting, replace **.UseIISIntegration()** with **.UseIIS()**, otherwise you'll get startup errors.
-
-.NET Core 1.0
-^^^^^^^^^^^^^
-
-**Install NuGet package**
-
-Install Ocelot and it's dependecies using nuget. You will need to create a netcoreapp1.0+ projct and bring the package into it. Then follow the Startup below and :doc:`../features/configuration` sections
-to get up and running. Please note you will need to choose one of the Ocelot packages from the NuGet feed.
-
-All versions can be found `here `_.
-
-**Configuration**
-
-The following is a very basic ocelot.json. It won't do anything but should get Ocelot starting.
-
-.. code-block:: json
-
- {
- "ReRoutes": [],
- "GlobalConfiguration": {}
- }
-
-**Program**
-
-Then in your Program.cs you will want to have the following.
-
-.. code-block:: csharp
-
- public class Program
- {
- public static void Main(string[] args)
- {
- IWebHostBuilder builder = new WebHostBuilder();
-
- builder.ConfigureServices(s => {
- });
-
- builder.UseKestrel()
- .UseContentRoot(Directory.GetCurrentDirectory())
- .UseStartup();
-
- var host = builder.Build();
-
- host.Run();
- }
- }
-
-**Startup**
-
-An example startup using a json file for configuration can be seen below.
-
-.. code-block:: csharp
-
- public class Startup
- {
- public Startup(IHostingEnvironment env)
- {
- var builder = new ConfigurationBuilder()
- .SetBasePath(env.ContentRootPath)
- .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
- .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
- .AddJsonFile("ocelot.json")
- .AddEnvironmentVariables();
-
- Configuration = builder.Build();
- }
-
- public IConfigurationRoot Configuration { get; }
-
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddOcelot(Configuration);
- }
-
- public void Configure(IApplicationBuilder app)
- {
- app.UseOcelot().Wait();
- }
- }
-
-This is pretty much all you need to get going.
+ }
\ No newline at end of file
diff --git a/docs/introduction/gotchas.rst b/docs/introduction/gotchas.rst
new file mode 100644
index 000000000..e980e0370
--- /dev/null
+++ b/docs/introduction/gotchas.rst
@@ -0,0 +1,4 @@
+Gotchas
+=============
+
+**Note:** When using ASP.NET Core 2.2 and you want to use In-Process hosting, replace **.UseIISIntegration()** with **.UseIIS()**, otherwise you'll get startup errors.
\ No newline at end of file
diff --git a/docs/introduction/notsupported.rst b/docs/introduction/notsupported.rst
index 404b8c70f..4d86759a0 100644
--- a/docs/introduction/notsupported.rst
+++ b/docs/introduction/notsupported.rst
@@ -7,10 +7,7 @@ Ocelot does not support...
* Forwarding a host header - The host header that you send to Ocelot will not be forwarded to the downstream service. Obviously this would break everything :(
-* Swagger - I have looked multiple times at building swagger.json out of the Ocelot ocelot.json but it doesnt fit into the vision
- I have for Ocelot. If you would like to have Swagger in Ocelot then you must roll your own swagger.json and do the following in your
- Startup.cs or Program.cs. The code sample below registers a piece of middleware that loads your hand rolled swagger.json and returns
- it on /swagger/v1/swagger.json. It then registers the SwaggerUI middleware from Swashbuckle.AspNetCore
+* Swagger - I have looked multiple times at building swagger.json out of the Ocelot ocelot.json but it doesnt fit into the vision I have for Ocelot. If you would like to have Swagger in Ocelot then you must roll your own swagger.json and do the following in your Startup.cs or Program.cs. The code sample below registers a piece of middleware that loads your hand rolled swagger.json and returns it on /swagger/v1/swagger.json. It then registers the SwaggerUI middleware from Swashbuckle.AspNetCore
.. code-block:: csharp
@@ -28,16 +25,8 @@ Ocelot does not support...
app.UseOcelot().Wait();
-The main reasons why I don't think Swagger makes sense is we already hand roll our definition in ocelot.json.
-If we want people developing against Ocelot to be able to see what routes are available then either share the ocelot.json
-with them (This should be as easy as granting access to a repo etc) or use the Ocelot administration API so that they can query Ocelot for the configuration.
+The main reasons why I don't think Swagger makes sense is we already hand roll our definition in ocelot.json. If we want people developing against Ocelot to be able to see what routes are available then either share the ocelot.json with them (This should be as easy as granting access to a repo etc) or use the Ocelot administration API so that they can query Ocelot for the configuration.
-In addition to this many people will configure Ocelot to proxy all traffic like /products/{everything} to their product service
-and you would not be describing what is actually available if you parsed this and turned it into a Swagger path. Also Ocelot has
-no concept of the models that the downstream services can return and linking to the above problem the same endpoint can return
-multiple models. Ocelot does not know what models might be used in POST, PUT etc so it all gets a bit messy and finally the Swashbuckle
-package doesnt reload swagger.json if it changes during runtime. Ocelot's configuration can change during runtime so the Swagger and Ocelot
-information would not match. Unless I rolled my own Swagger implementation.
+In addition to this many people will configure Ocelot to proxy all traffic like /products/{everything} to their product service and you would not be describing what is actually available if you parsed this and turned it into a Swagger path. Also Ocelot has no concept of the models that the downstream services can return and linking to the above problem the same endpoint can return multiple models. Ocelot does not know what models might be used in POST, PUT etc so it all gets a bit messy and finally the Swashbuckle package doesnt reload swagger.json if it changes during runtime. Ocelot's configuration can change during runtime so the Swagger and Ocelot information would not match. Unless I rolled my own Swagger implementation.
-If the user wants something to easily test against the Ocelot API then I suggest using Postman as a simple way to do this. It might
-even be possible to write something that maps ocelot.json to the postman json spec. However I don't intend to do this.
+If the user wants something to easily test against the Ocelot API then I suggest using Postman as a simple way to do this. It might even be possible to write something that maps ocelot.json to the postman json spec. However I don't intend to do this.
diff --git a/images/ocelot_logo.png b/images/ocelot_logo.png
new file mode 100644
index 000000000..78cae888b
Binary files /dev/null and b/images/ocelot_logo.png differ
diff --git a/releasenotes.md b/releasenotes.md
new file mode 100644
index 000000000..e69de29bb
diff --git a/samples/AdministrationApi/AdministrationApi.csproj b/samples/AdministrationApi/AdministrationApi.csproj
index 3fcf5dc8e..59739bdee 100644
--- a/samples/AdministrationApi/AdministrationApi.csproj
+++ b/samples/AdministrationApi/AdministrationApi.csproj
@@ -1,13 +1,13 @@
-
-
- netcoreapp3.1
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
+
+ net7.0
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/AdministrationApi/Issue645.postman_collection.json b/samples/AdministrationApi/Issue645.postman_collection.json
index 580419a0b..c9bac89f5 100644
--- a/samples/AdministrationApi/Issue645.postman_collection.json
+++ b/samples/AdministrationApi/Issue645.postman_collection.json
@@ -1,150 +1,150 @@
-{
- "info": {
- "_postman_id": "6234b40a-e363-4c73-8577-1c9074abb951",
- "name": "Issue645",
- "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
- },
- "item": [
- {
- "name": "1. GET http://localhost: 55580/administration/.well-known/openid-configuration",
- "request": {
- "method": "GET",
- "header": [
- {
- "key": "Authorization",
- "value": "Bearer {{AccessToken}}"
- }
- ],
- "body": {},
- "url": {
- "raw": "http://localhost:5000/administration/.well-known/openid-configuration",
- "protocol": "http",
- "host": [
- "localhost"
- ],
- "port": "5000",
- "path": [
- "administration",
- ".well-known",
- "openid-configuration"
- ]
- }
- },
- "response": []
- },
- {
- "name": "3. GET http://localhost: 55580/administration/configuration",
- "request": {
- "method": "POST",
- "header": [
- {
- "key": "Authorization",
- "value": "Bearer {{AccessToken}}"
- },
- {
- "key": "Content-Type",
- "value": "application/json"
- }
- ],
- "body": {
- "mode": "raw",
- "raw": "{\r\n \"reRoutes\": [\r\n {\r\n \"downstreamPathTemplate\": \"/{everything}\",\r\n \"upstreamPathTemplate\": \"/templates/{everything}\",\r\n \"upstreamHttpMethod\": [\r\n \"GET\"\r\n ],\r\n \"addHeadersToRequest\": {},\r\n \"upstreamHeaderTransform\": {},\r\n \"downstreamHeaderTransform\": {},\r\n \"addClaimsToRequest\": {},\r\n \"routeClaimsRequirement\": {},\r\n \"addQueriesToRequest\": {},\r\n \"requestIdKey\": null,\r\n \"fileCacheOptions\": {\r\n \"ttlSeconds\": 0,\r\n \"region\": null\r\n },\r\n \"reRouteIsCaseSensitive\": false,\r\n \"downstreamScheme\": \"http\",\r\n \"qoSOptions\": {\r\n \"exceptionsAllowedBeforeBreaking\": 0,\r\n \"durationOfBreak\": 0,\r\n \"timeoutValue\": 0\r\n },\r\n \"loadBalancerOptions\": {\r\n \"type\": null,\r\n \"key\": null,\r\n \"expiry\": 0\r\n },\r\n \"rateLimitOptions\": {\r\n \"clientWhitelist\": [],\r\n \"enableRateLimiting\": false,\r\n \"period\": null,\r\n \"periodTimespan\": 0,\r\n \"limit\": 0\r\n },\r\n \"authenticationOptions\": {\r\n \"authenticationProviderKey\": null,\r\n \"allowedScopes\": []\r\n },\r\n \"httpHandlerOptions\": {\r\n \"allowAutoRedirect\": false,\r\n \"useCookieContainer\": false,\r\n \"useTracing\": false,\r\n \"useProxy\": true\r\n },\r\n \"downstreamHostAndPorts\": [\r\n {\r\n \"host\": \"localhost\",\r\n \"port\": 50689\r\n }\r\n ],\r\n \"upstreamHost\": null,\r\n \"key\": null,\r\n \"delegatingHandlers\": [],\r\n \"priority\": 1,\r\n \"timeout\": 0,\r\n \"dangerousAcceptAnyServerCertificateValidator\": false\r\n }\r\n ],\r\n \"aggregates\": [],\r\n \"globalConfiguration\": {\r\n \"requestIdKey\": \"Request-Id\",\r\n \"rateLimitOptions\": {\r\n \"clientIdHeader\": \"ClientId\",\r\n \"quotaExceededMessage\": null,\r\n \"rateLimitCounterPrefix\": \"ocelot\",\r\n \"disableRateLimitHeaders\": false,\r\n \"httpStatusCode\": 429\r\n },\r\n \"qoSOptions\": {\r\n \"exceptionsAllowedBeforeBreaking\": 0,\r\n \"durationOfBreak\": 0,\r\n \"timeoutValue\": 0\r\n },\r\n \"baseUrl\": \"http://localhost:55580\",\r\n \"loadBalancerOptions\": {\r\n \"type\": null,\r\n \"key\": null,\r\n \"expiry\": 0\r\n },\r\n \"downstreamScheme\": null,\r\n \"httpHandlerOptions\": {\r\n \"allowAutoRedirect\": false,\r\n \"useCookieContainer\": false,\r\n \"useTracing\": false,\r\n \"useProxy\": true\r\n }\r\n }\r\n}"
- },
- "url": {
- "raw": "http://localhost:5000/administration/configuration",
- "protocol": "http",
- "host": [
- "localhost"
- ],
- "port": "5000",
- "path": [
- "administration",
- "configuration"
- ]
- }
- },
- "response": []
- },
- {
- "name": "2. POST http://localhost: 55580/administration/connect/token",
- "event": [
- {
- "listen": "test",
- "script": {
- "type": "text/javascript",
- "exec": [
- "var jsonData = JSON.parse(responseBody);",
- "postman.setGlobalVariable(\"AccessToken\", jsonData.access_token);",
- "postman.setGlobalVariable(\"RefreshToken\", jsonData.refresh_token);"
- ]
- }
- }
- ],
- "request": {
- "method": "POST",
- "header": [],
- "body": {
- "mode": "formdata",
- "formdata": [
- {
- "key": "client_id",
- "value": "admin",
- "type": "text"
- },
- {
- "key": "client_secret",
- "value": "secret",
- "type": "text"
- },
- {
- "key": "scope",
- "value": "admin",
- "type": "text"
- },
- {
- "key": "grant_type",
- "value": "client_credentials",
- "type": "text"
- }
- ]
- },
- "url": {
- "raw": "http://localhost:5000/administration/connect/token",
- "protocol": "http",
- "host": [
- "localhost"
- ],
- "port": "5000",
- "path": [
- "administration",
- "connect",
- "token"
- ]
- }
- },
- "response": []
- }
- ],
- "event": [
- {
- "listen": "prerequest",
- "script": {
- "id": "0f60e7b3-e4f1-4458-bbc4-fc4809e86b2d",
- "type": "text/javascript",
- "exec": [
- ""
- ]
- }
- },
- {
- "listen": "test",
- "script": {
- "id": "1279a2cf-b771-4a86-9dfa-302b240fac62",
- "type": "text/javascript",
- "exec": [
- ""
- ]
- }
- }
- ]
+{
+ "info": {
+ "_postman_id": "6234b40a-e363-4c73-8577-1c9074abb951",
+ "name": "Issue645",
+ "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
+ },
+ "item": [
+ {
+ "name": "1. GET http://localhost: 55580/administration/.well-known/openid-configuration",
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Authorization",
+ "value": "Bearer {{AccessToken}}"
+ }
+ ],
+ "body": {},
+ "url": {
+ "raw": "http://localhost:5000/administration/.well-known/openid-configuration",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "5000",
+ "path": [
+ "administration",
+ ".well-known",
+ "openid-configuration"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "3. GET http://localhost: 55580/administration/configuration",
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Authorization",
+ "value": "Bearer {{AccessToken}}"
+ },
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"routes\": [\r\n {\r\n \"downstreamPathTemplate\": \"/{everything}\",\r\n \"upstreamPathTemplate\": \"/templates/{everything}\",\r\n \"upstreamHttpMethod\": [\r\n \"GET\"\r\n ],\r\n \"addHeadersToRequest\": {},\r\n \"upstreamHeaderTransform\": {},\r\n \"downstreamHeaderTransform\": {},\r\n \"addClaimsToRequest\": {},\r\n \"routeClaimsRequirement\": {},\r\n \"addQueriesToRequest\": {},\r\n \"requestIdKey\": null,\r\n \"fileCacheOptions\": {\r\n \"ttlSeconds\": 0,\r\n \"region\": null\r\n },\r\n \"routeIsCaseSensitive\": false,\r\n \"downstreamScheme\": \"http\",\r\n \"qoSOptions\": {\r\n \"exceptionsAllowedBeforeBreaking\": 0,\r\n \"durationOfBreak\": 0,\r\n \"timeoutValue\": 0\r\n },\r\n \"loadBalancerOptions\": {\r\n \"type\": null,\r\n \"key\": null,\r\n \"expiry\": 0\r\n },\r\n \"rateLimitOptions\": {\r\n \"clientWhitelist\": [],\r\n \"enableRateLimiting\": false,\r\n \"period\": null,\r\n \"periodTimespan\": 0,\r\n \"limit\": 0\r\n },\r\n \"authenticationOptions\": {\r\n \"authenticationProviderKey\": null,\r\n \"allowedScopes\": []\r\n },\r\n \"httpHandlerOptions\": {\r\n \"allowAutoRedirect\": false,\r\n \"useCookieContainer\": false,\r\n \"useTracing\": false,\r\n \"useProxy\": true\r\n },\r\n \"downstreamHostAndPorts\": [\r\n {\r\n \"host\": \"localhost\",\r\n \"port\": 50689\r\n }\r\n ],\r\n \"upstreamHost\": null,\r\n \"key\": null,\r\n \"delegatingHandlers\": [],\r\n \"priority\": 1,\r\n \"timeout\": 0,\r\n \"dangerousAcceptAnyServerCertificateValidator\": false\r\n }\r\n ],\r\n \"aggregates\": [],\r\n \"globalConfiguration\": {\r\n \"requestIdKey\": \"Request-Id\",\r\n \"rateLimitOptions\": {\r\n \"clientIdHeader\": \"ClientId\",\r\n \"quotaExceededMessage\": null,\r\n \"rateLimitCounterPrefix\": \"ocelot\",\r\n \"disableRateLimitHeaders\": false,\r\n \"httpStatusCode\": 429\r\n },\r\n \"qoSOptions\": {\r\n \"exceptionsAllowedBeforeBreaking\": 0,\r\n \"durationOfBreak\": 0,\r\n \"timeoutValue\": 0\r\n },\r\n \"baseUrl\": \"http://localhost:55580\",\r\n \"loadBalancerOptions\": {\r\n \"type\": null,\r\n \"key\": null,\r\n \"expiry\": 0\r\n },\r\n \"downstreamScheme\": null,\r\n \"httpHandlerOptions\": {\r\n \"allowAutoRedirect\": false,\r\n \"useCookieContainer\": false,\r\n \"useTracing\": false,\r\n \"useProxy\": true\r\n }\r\n }\r\n}"
+ },
+ "url": {
+ "raw": "http://localhost:5000/administration/configuration",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "5000",
+ "path": [
+ "administration",
+ "configuration"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "2. POST http://localhost: 55580/administration/connect/token",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "type": "text/javascript",
+ "exec": [
+ "var jsonData = JSON.parse(responseBody);",
+ "postman.setGlobalVariable(\"AccessToken\", jsonData.access_token);",
+ "postman.setGlobalVariable(\"RefreshToken\", jsonData.refresh_token);"
+ ]
+ }
+ }
+ ],
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "formdata",
+ "formdata": [
+ {
+ "key": "client_id",
+ "value": "admin",
+ "type": "text"
+ },
+ {
+ "key": "client_secret",
+ "value": "secret",
+ "type": "text"
+ },
+ {
+ "key": "scope",
+ "value": "admin",
+ "type": "text"
+ },
+ {
+ "key": "grant_type",
+ "value": "client_credentials",
+ "type": "text"
+ }
+ ]
+ },
+ "url": {
+ "raw": "http://localhost:5000/administration/connect/token",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "5000",
+ "path": [
+ "administration",
+ "connect",
+ "token"
+ ]
+ }
+ },
+ "response": []
+ }
+ ],
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "id": "0f60e7b3-e4f1-4458-bbc4-fc4809e86b2d",
+ "type": "text/javascript",
+ "exec": [
+ string.Empty
+ ]
+ }
+ },
+ {
+ "listen": "test",
+ "script": {
+ "id": "1279a2cf-b771-4a86-9dfa-302b240fac62",
+ "type": "text/javascript",
+ "exec": [
+ string.Empty
+ ]
+ }
+ }
+ ]
}
\ No newline at end of file
diff --git a/samples/AdministrationApi/Program.cs b/samples/AdministrationApi/Program.cs
index a5e46fda0..36695bf9a 100644
--- a/samples/AdministrationApi/Program.cs
+++ b/samples/AdministrationApi/Program.cs
@@ -1,50 +1,48 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore;
+using System.IO;
+
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
+
+using Ocelot.Administration;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
-using Ocelot.Administration;
namespace AdministrationApi
{
- public class Program
-{
- public static void Main(string[] args)
+ public class Program
{
- new WebHostBuilder()
- .UseKestrel()
- .UseUrls("http://localhost:5000")
- .UseContentRoot(Directory.GetCurrentDirectory())
- .ConfigureAppConfiguration((hostingContext, config) =>
- {
- config
- .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
- .AddJsonFile("appsettings.json", true, true)
- .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
- .AddJsonFile("ocelot.json")
- .AddEnvironmentVariables();
- })
- .ConfigureServices(s => {
- s.AddOcelot()
- .AddAdministration("/administration", "secret");
- })
- .ConfigureLogging((hostingContext, logging) =>
- {
- logging.AddConsole();
- })
- .UseIISIntegration()
- .Configure(app =>
- {
- app.UseOcelot().Wait();
- })
- .Build()
- .Run();
+ public static void Main(string[] args)
+ {
+ new WebHostBuilder()
+ .UseKestrel()
+ .UseUrls("http://localhost:5000")
+ .UseContentRoot(Directory.GetCurrentDirectory())
+ .ConfigureAppConfiguration((hostingContext, config) =>
+ {
+ config
+ .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
+ .AddJsonFile("appsettings.json", true, true)
+ .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
+ .AddJsonFile("ocelot.json")
+ .AddEnvironmentVariables();
+ })
+ .ConfigureServices(s =>
+ {
+ s.AddOcelot()
+ .AddAdministration("/administration", "secret");
+ })
+ .ConfigureLogging((hostingContext, logging) =>
+ {
+ logging.AddConsole();
+ })
+ .UseIISIntegration()
+ .Configure(app =>
+ {
+ app.UseOcelot().Wait();
+ })
+ .Build()
+ .Run();
+ }
}
}
-}
diff --git a/samples/AdministrationApi/README.md b/samples/AdministrationApi/README.md
index 36ed2a677..42a01b2f4 100644
--- a/samples/AdministrationApi/README.md
+++ b/samples/AdministrationApi/README.md
@@ -1,94 +1,94 @@
-```json
-{
- "reRoutes": [
- {
- "downstreamPathTemplate": "/{everything}",
- "upstreamPathTemplate": "/templates/{everything}",
- "upstreamHttpMethod": [
- "GET"
- ],
- "addHeadersToRequest": {},
- "upstreamHeaderTransform": {},
- "downstreamHeaderTransform": {},
- "addClaimsToRequest": {},
- "routeClaimsRequirement": {},
- "addQueriesToRequest": {},
- "requestIdKey": null,
- "fileCacheOptions": {
- "ttlSeconds": 0,
- "region": null
- },
- "reRouteIsCaseSensitive": false,
- "downstreamScheme": "http",
- "qoSOptions": {
- "exceptionsAllowedBeforeBreaking": 0,
- "durationOfBreak": 0,
- "timeoutValue": 0
- },
- "loadBalancerOptions": {
- "type": null,
- "key": null,
- "expiry": 0
- },
- "rateLimitOptions": {
- "clientWhitelist": [],
- "enableRateLimiting": false,
- "period": null,
- "periodTimespan": 0,
- "limit": 0
- },
- "authenticationOptions": {
- "authenticationProviderKey": null,
- "allowedScopes": []
- },
- "httpHandlerOptions": {
- "allowAutoRedirect": false,
- "useCookieContainer": false,
- "useTracing": false,
- "useProxy": true
- },
- "downstreamHostAndPorts": [
- {
- "host": "localhost",
- "port": 50689
- }
- ],
- "upstreamHost": null,
- "key": null,
- "delegatingHandlers": [],
- "priority": 1,
- "timeout": 0,
- "dangerousAcceptAnyServerCertificateValidator": false
- }
- ],
- "aggregates": [],
- "globalConfiguration": {
- "requestIdKey": "Request-Id",
- "rateLimitOptions": {
- "clientIdHeader": "ClientId",
- "quotaExceededMessage": null,
- "rateLimitCounterPrefix": "ocelot",
- "disableRateLimitHeaders": false,
- "httpStatusCode": 429
- },
- "qoSOptions": {
- "exceptionsAllowedBeforeBreaking": 0,
- "durationOfBreak": 0,
- "timeoutValue": 0
- },
- "baseUrl": "http://localhost:55580",
- "loadBalancerOptions": {
- "type": null,
- "key": null,
- "expiry": 0
- },
- "downstreamScheme": null,
- "httpHandlerOptions": {
- "allowAutoRedirect": false,
- "useCookieContainer": false,
- "useTracing": false,
- "useProxy": true
- }
- }
-}
-```
+```json
+{
+ "routes": [
+ {
+ "downstreamPathTemplate": "/{everything}",
+ "upstreamPathTemplate": "/templates/{everything}",
+ "upstreamHttpMethod": [
+ "GET"
+ ],
+ "addHeadersToRequest": {},
+ "upstreamHeaderTransform": {},
+ "downstreamHeaderTransform": {},
+ "addClaimsToRequest": {},
+ "routeClaimsRequirement": {},
+ "addQueriesToRequest": {},
+ "requestIdKey": null,
+ "fileCacheOptions": {
+ "ttlSeconds": 0,
+ "region": null
+ },
+ "routeIsCaseSensitive": false,
+ "downstreamScheme": "http",
+ "qoSOptions": {
+ "exceptionsAllowedBeforeBreaking": 0,
+ "durationOfBreak": 0,
+ "timeoutValue": 0
+ },
+ "loadBalancerOptions": {
+ "type": null,
+ "key": null,
+ "expiry": 0
+ },
+ "rateLimitOptions": {
+ "clientWhitelist": [],
+ "enableRateLimiting": false,
+ "period": null,
+ "periodTimespan": 0,
+ "limit": 0
+ },
+ "authenticationOptions": {
+ "authenticationProviderKey": null,
+ "allowedScopes": []
+ },
+ "httpHandlerOptions": {
+ "allowAutoRedirect": false,
+ "useCookieContainer": false,
+ "useTracing": false,
+ "useProxy": true
+ },
+ "downstreamHostAndPorts": [
+ {
+ "host": "localhost",
+ "port": 50689
+ }
+ ],
+ "upstreamHost": null,
+ "key": null,
+ "delegatingHandlers": [],
+ "priority": 1,
+ "timeout": 0,
+ "dangerousAcceptAnyServerCertificateValidator": false
+ }
+ ],
+ "aggregates": [],
+ "globalConfiguration": {
+ "requestIdKey": "Request-Id",
+ "rateLimitOptions": {
+ "clientIdHeader": "ClientId",
+ "quotaExceededMessage": null,
+ "rateLimitCounterPrefix": "ocelot",
+ "disableRateLimitHeaders": false,
+ "httpStatusCode": 429
+ },
+ "qoSOptions": {
+ "exceptionsAllowedBeforeBreaking": 0,
+ "durationOfBreak": 0,
+ "timeoutValue": 0
+ },
+ "baseUrl": "http://localhost:55580",
+ "loadBalancerOptions": {
+ "type": null,
+ "key": null,
+ "expiry": 0
+ },
+ "downstreamScheme": null,
+ "httpHandlerOptions": {
+ "allowAutoRedirect": false,
+ "useCookieContainer": false,
+ "useTracing": false,
+ "useProxy": true
+ }
+ }
+}
+```
diff --git a/samples/AdministrationApi/ocelot.json b/samples/AdministrationApi/ocelot.json
index d16dcebfc..0fa4143ff 100644
--- a/samples/AdministrationApi/ocelot.json
+++ b/samples/AdministrationApi/ocelot.json
@@ -1,18 +1,18 @@
-{
- "ReRoutes": [
- {
- "DownstreamPathTemplate": "/service/stats/collected",
- "DownstreamScheme": "http",
- "DownstreamHostAndPorts": [
- {
- "Host": "localhost",
- "Port": 5100
- }
- ],
- "UpstreamPathTemplate": "/api/stats/collected"
- }
- ],
- "GlobalConfiguration": {
- "BaseUrl": "http://localhost:5000"
- }
+{
+ "Routes": [
+ {
+ "DownstreamPathTemplate": "/service/stats/collected",
+ "DownstreamScheme": "http",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "localhost",
+ "Port": 5100
+ }
+ ],
+ "UpstreamPathTemplate": "/api/stats/collected"
+ }
+ ],
+ "GlobalConfiguration": {
+ "BaseUrl": "http://localhost:5000"
+ }
}
\ No newline at end of file
diff --git a/samples/Docker/Dockerfile b/samples/Docker/Dockerfile
index 86b12ab01..dad54b20f 100644
--- a/samples/Docker/Dockerfile
+++ b/samples/Docker/Dockerfile
@@ -17,7 +17,6 @@ COPY src/Ocelot.Cache.CacheManager/Ocelot.Cache.CacheManager.csproj src/Ocelot.C
COPY src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj
COPY src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj
COPY src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj
-COPY src/Ocelot.Provider.Rafty/Ocelot.Provider.Rafty.csproj src/Ocelot.Provider.Rafty/Ocelot.Provider.Rafty.csproj
COPY src/Ocelot.Tracing.Butterfly/Ocelot.Tracing.Butterfly.csproj src/Ocelot.Tracing.Butterfly/Ocelot.Tracing.Butterfly.csproj
COPY src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj
COPY test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj
diff --git a/samples/OcelotBasic/OcelotBasic.csproj b/samples/OcelotBasic/Ocelot.Samples.OcelotBasic.ApiGateway.csproj
similarity index 51%
rename from samples/OcelotBasic/OcelotBasic.csproj
rename to samples/OcelotBasic/Ocelot.Samples.OcelotBasic.ApiGateway.csproj
index 5e2902064..a6e612a78 100644
--- a/samples/OcelotBasic/OcelotBasic.csproj
+++ b/samples/OcelotBasic/Ocelot.Samples.OcelotBasic.ApiGateway.csproj
@@ -1,16 +1,16 @@
-
-
-
- netcoreapp3.1
-
-
+
+
+
+ net7.0
+ InProcess
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/samples/OcelotBasic/Program.cs b/samples/OcelotBasic/Program.cs
index c0d8bed6a..b5856d857 100644
--- a/samples/OcelotBasic/Program.cs
+++ b/samples/OcelotBasic/Program.cs
@@ -1,42 +1,40 @@
-using System.IO;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.Hosting;
-using Ocelot.DependencyInjection;
-using Ocelot.Middleware;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using System.IO;
+
+namespace Ocelot.Samples.OcelotBasic.ApiGateway;
-namespace OcelotBasic
+public class Program
{
- public class Program
+ public static void Main(string[] args)
{
- public static void Main(string[] args)
- {
- new WebHostBuilder()
- .UseKestrel()
- .UseContentRoot(Directory.GetCurrentDirectory())
- .ConfigureAppConfiguration((hostingContext, config) =>
- {
- config
- .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
- .AddJsonFile("appsettings.json", true, true)
- .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
- .AddJsonFile("ocelot.json")
- .AddEnvironmentVariables();
- })
- .ConfigureServices(s => {
- s.AddOcelot();
- })
- .ConfigureLogging((hostingContext, logging) =>
- {
- //add your logging
- })
- .UseIISIntegration()
- .Configure(app =>
- {
- app.UseOcelot().Wait();
- })
- .Build()
- .Run();
- }
+ new WebHostBuilder()
+ .UseKestrel()
+ .UseContentRoot(Directory.GetCurrentDirectory())
+ .ConfigureAppConfiguration((hostingContext, config) =>
+ {
+ config
+ .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
+ .AddJsonFile("appsettings.json", true, true)
+ .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
+ .AddJsonFile("ocelot.json")
+ .AddEnvironmentVariables();
+ })
+ .ConfigureLogging((hostingContext, logging) =>
+ {
+ if (hostingContext.HostingEnvironment.IsDevelopment())
+ {
+ logging.ClearProviders();
+ logging.AddConsole();
+ }
+ //add your logging
+ })
+ .UseIISIntegration()
+ .UseStartup()
+ .Build()
+ .Run();
}
}
diff --git a/samples/OcelotBasic/Properties/launchSettings.json b/samples/OcelotBasic/Properties/launchSettings.json
index b500ae576..924e292df 100644
--- a/samples/OcelotBasic/Properties/launchSettings.json
+++ b/samples/OcelotBasic/Properties/launchSettings.json
@@ -11,17 +11,19 @@
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
+ "launchUrl": "posts/1",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
- "OcelotBasic": {
+ "ApiGateway": {
"commandName": "Project",
"launchBrowser": true,
+ "launchUrl": "posts/1",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:5001;http://localhost:5000"
}
}
-}
\ No newline at end of file
+}
diff --git a/samples/OcelotBasic/Startup.cs b/samples/OcelotBasic/Startup.cs
new file mode 100644
index 000000000..62b8b422b
--- /dev/null
+++ b/samples/OcelotBasic/Startup.cs
@@ -0,0 +1,30 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Ocelot.DependencyInjection;
+using Ocelot.Middleware;
+
+namespace Ocelot.Samples.OcelotBasic.ApiGateway;
+
+public class Startup
+{
+ // This method gets called by the runtime. Use this method to add services to the container.
+ // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services.AddOcelot();
+ services.AddLogging();
+ }
+
+ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+ {
+ if (env.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ }
+
+ app.UseOcelot().Wait();
+ }
+}
diff --git a/samples/OcelotBasic/appsettings.Development.json b/samples/OcelotBasic/appsettings.Development.json
index dba68eb12..07053a355 100644
--- a/samples/OcelotBasic/appsettings.Development.json
+++ b/samples/OcelotBasic/appsettings.Development.json
@@ -1,9 +1,11 @@
{
"Logging": {
"LogLevel": {
- "Default": "Information",
- "Microsoft": "Warning",
- "Microsoft.Hosting.Lifetime": "Information"
+ "Default": "Debug",
+ "Microsoft": "Information",
+ "Microsoft.AspNetCore": "Information",
+ "Microsoft.Hosting.Lifetime": "Information",
+ "System": "Information"
}
}
}
diff --git a/samples/OcelotBasic/appsettings.json b/samples/OcelotBasic/appsettings.json
index 81ff87771..7376aada1 100644
--- a/samples/OcelotBasic/appsettings.json
+++ b/samples/OcelotBasic/appsettings.json
@@ -1,9 +1,7 @@
{
"Logging": {
"LogLevel": {
- "Default": "Information",
- "Microsoft": "Warning",
- "Microsoft.Hosting.Lifetime": "Information"
+ "Default": "Warning"
}
},
"AllowedHosts": "*"
diff --git a/samples/OcelotBasic/ocelot.json b/samples/OcelotBasic/ocelot.json
index 7a1759ffa..2864550cd 100644
--- a/samples/OcelotBasic/ocelot.json
+++ b/samples/OcelotBasic/ocelot.json
@@ -1,5 +1,5 @@
{
- "ReRoutes": [
+ "Routes": [
{
"DownstreamPathTemplate": "/todos/{id}",
"DownstreamScheme": "https",
@@ -18,4 +18,4 @@
"GlobalConfiguration": {
"BaseUrl": "https://localhost:5000"
}
-}
\ No newline at end of file
+}
diff --git a/samples/OcelotEureka/ApiGateway/ApiGateway.csproj b/samples/OcelotEureka/ApiGateway/ApiGateway.csproj
index a257c9801..a286c8a16 100644
--- a/samples/OcelotEureka/ApiGateway/ApiGateway.csproj
+++ b/samples/OcelotEureka/ApiGateway/ApiGateway.csproj
@@ -1,7 +1,7 @@
- netcoreapp3.1
+ net7.0
@@ -15,9 +15,9 @@
-
-
-
+
+
+
diff --git a/samples/OcelotEureka/ApiGateway/Program.cs b/samples/OcelotEureka/ApiGateway/Program.cs
index f2cd87f92..f80237b4c 100644
--- a/samples/OcelotEureka/ApiGateway/Program.cs
+++ b/samples/OcelotEureka/ApiGateway/Program.cs
@@ -1,14 +1,14 @@
using Ocelot.Provider.Eureka;
using Ocelot.Provider.Polly;
+using Microsoft.AspNetCore;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+
+using Ocelot.DependencyInjection;
+using Ocelot.Middleware;
namespace ApiGateway
{
- using Microsoft.AspNetCore;
- using Microsoft.AspNetCore.Hosting;
- using Microsoft.Extensions.Configuration;
- using Ocelot.DependencyInjection;
- using Ocelot.Middleware;
-
public class Program
{
public static void Main(string[] args)
diff --git a/samples/OcelotEureka/ApiGateway/ocelot.json b/samples/OcelotEureka/ApiGateway/ocelot.json
index 963160f9b..5a69973de 100644
--- a/samples/OcelotEureka/ApiGateway/ocelot.json
+++ b/samples/OcelotEureka/ApiGateway/ocelot.json
@@ -1,22 +1,22 @@
-{
- "ReRoutes": [
- {
- "DownstreamPathTemplate": "/api/Category",
- "DownstreamScheme": "http",
- "UpstreamPathTemplate": "/Category",
- "ServiceName": "ncore-rat",
- "UpstreamHttpMethod": [ "Get" ],
- "QoSOptions": {
- "ExceptionsAllowedBeforeBreaking": 3,
- "DurationOfBreak": 10000,
- "TimeoutValue": 5000
- },
- "FileCacheOptions": { "TtlSeconds": 15 }
- }
- ],
- "GlobalConfiguration": {
- "RequestIdKey": "OcRequestId",
- "AdministrationPath": "/administration",
- "ServiceDiscoveryProvider": { "Type": "Eureka" }
- }
-}
+{
+ "Routes": [
+ {
+ "DownstreamPathTemplate": "/api/Category",
+ "DownstreamScheme": "http",
+ "UpstreamPathTemplate": "/Category",
+ "ServiceName": "ncore-rat",
+ "UpstreamHttpMethod": [ "Get" ],
+ "QoSOptions": {
+ "ExceptionsAllowedBeforeBreaking": 3,
+ "DurationOfBreak": 10000,
+ "TimeoutValue": 5000
+ },
+ "FileCacheOptions": { "TtlSeconds": 15 }
+ }
+ ],
+ "GlobalConfiguration": {
+ "RequestIdKey": "OcRequestId",
+ "AdministrationPath": "/administration",
+ "ServiceDiscoveryProvider": { "Type": "Eureka" }
+ }
+}
diff --git a/samples/OcelotEureka/DownstreamService/Controllers/CategoryController.cs b/samples/OcelotEureka/DownstreamService/Controllers/CategoryController.cs
index 53350be74..5ce38f475 100644
--- a/samples/OcelotEureka/DownstreamService/Controllers/CategoryController.cs
+++ b/samples/OcelotEureka/DownstreamService/Controllers/CategoryController.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+
using Microsoft.AspNetCore.Mvc;
namespace DownstreamService.Controllers
diff --git a/samples/OcelotEureka/DownstreamService/DownstreamService.csproj b/samples/OcelotEureka/DownstreamService/DownstreamService.csproj
index 209fa6d21..748249c00 100644
--- a/samples/OcelotEureka/DownstreamService/DownstreamService.csproj
+++ b/samples/OcelotEureka/DownstreamService/DownstreamService.csproj
@@ -1,7 +1,7 @@
- netcoreapp3.1
+ net7.0
diff --git a/samples/OcelotEureka/DownstreamService/Program.cs b/samples/OcelotEureka/DownstreamService/Program.cs
index ddce0c286..130b46f0e 100644
--- a/samples/OcelotEureka/DownstreamService/Program.cs
+++ b/samples/OcelotEureka/DownstreamService/Program.cs
@@ -1,4 +1,5 @@
using System;
+
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
diff --git a/samples/OcelotEureka/DownstreamService/Startup.cs b/samples/OcelotEureka/DownstreamService/Startup.cs
index b3924be64..56a187c2c 100644
--- a/samples/OcelotEureka/DownstreamService/Startup.cs
+++ b/samples/OcelotEureka/DownstreamService/Startup.cs
@@ -1,18 +1,13 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
+using Microsoft.Extensions.Hosting;
+
+using Steeltoe.Discovery.Client;
namespace DownstreamService
{
- using Steeltoe.Discovery.Client;
-
public class Startup
{
public Startup(IConfiguration configuration)
@@ -30,7 +25,7 @@ public void ConfigureServices(IServiceCollection services)
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
- public void Configure(IApplicationBuilder app, IHostingEnvironment env)
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
@@ -38,7 +33,6 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
}
app.UseDiscoveryClient();
- app.UseMvc();
}
}
}
diff --git a/samples/OcelotGraphQL/OcelotGraphQL.csproj b/samples/OcelotGraphQL/OcelotGraphQL.csproj
index 408d8543e..06956a7f8 100644
--- a/samples/OcelotGraphQL/OcelotGraphQL.csproj
+++ b/samples/OcelotGraphQL/OcelotGraphQL.csproj
@@ -1,6 +1,6 @@
- netcoreapp3.1
+ net7.0
@@ -11,7 +11,7 @@
-
-
+
+
-
\ No newline at end of file
+
diff --git a/samples/OcelotGraphQL/Program.cs b/samples/OcelotGraphQL/Program.cs
index 83a48fda1..bbf5eff48 100644
--- a/samples/OcelotGraphQL/Program.cs
+++ b/samples/OcelotGraphQL/Program.cs
@@ -1,131 +1,137 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.Logging;
-using Ocelot.Middleware;
-using Ocelot.DependencyInjection;
-using GraphQL.Types;
-using GraphQL;
-using Ocelot.Requester;
-using Ocelot.Responses;
-using System.Net.Http;
-using System.Net;
-using Microsoft.Extensions.DependencyInjection;
-using System.Threading;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
-namespace OcelotGraphQL
-{
- public class Hero
- {
- public int Id { get; set; }
- public string Name { get; set; }
- }
+using GraphQL;
+using GraphQL.Types;
- public class Query
- {
- private List _heroes = new List
- {
- new Hero { Id = 1, Name = "R2-D2" },
- new Hero { Id = 2, Name = "Batman" },
- new Hero { Id = 3, Name = "Wonder Woman" },
- new Hero { Id = 4, Name = "Tom Pallister" }
- };
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
- [GraphQLMetadata("hero")]
- public Hero GetHero(int id)
- {
- return _heroes.FirstOrDefault(x => x.Id == id);
- }
- }
-
- public class GraphQlDelegatingHandler : DelegatingHandler
- {
- private readonly ISchema _schema;
-
- public GraphQlDelegatingHandler(ISchema schema)
- {
- _schema = schema;
- }
-
- protected async override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
- {
- //try get query from body, could check http method :)
- var query = await request.Content.ReadAsStringAsync();
-
- //if not body try query string, dont hack like this in real world..
- if(query.Length == 0)
- {
- var decoded = WebUtility.UrlDecode(request.RequestUri.Query);
- query = decoded.Replace("?query=", "");
- }
-
- var result = _schema.Execute(_ =>
- {
- _.Query = query;
- });
-
- //maybe check for errors and headers etc in real world?
- var response = new HttpResponseMessage(HttpStatusCode.OK)
- {
- Content = new StringContent(result)
- };
-
- //ocelot will treat this like any other http request...
- return response;
- }
- }
-
- public class Program
- {
- public static void Main(string[] args)
- {
- var schema = Schema.For(@"
- type Hero {
- id: Int
- name: String
- }
-
- type Query {
- hero(id: Int): Hero
- }
- ", _ => {
- _.Types.Include();
- });
-
- new WebHostBuilder()
- .UseKestrel()
- .UseContentRoot(Directory.GetCurrentDirectory())
- .ConfigureAppConfiguration((hostingContext, config) =>
- {
- config
- .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
- .AddJsonFile("appsettings.json", true, true)
- .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
- .AddJsonFile("ocelot.json", false, false)
- .AddEnvironmentVariables();
- })
- .ConfigureServices(s => {
- s.AddSingleton(schema);
- s.AddOcelot()
- .AddSingletonDelegatingHandler();
- })
- .ConfigureLogging((hostingContext, logging) =>
- {
- logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
- logging.AddConsole();
- })
- .UseIISIntegration()
- .Configure(app =>
- {
- app.UseOcelot().Wait();
- })
- .Build()
- .Run();
- }
- }
-}
+using Ocelot.DependencyInjection;
+using Ocelot.Middleware;
+
+namespace OcelotGraphQL
+{
+ public class Hero
+ {
+ public int Id { get; set; }
+ public string Name { get; set; }
+ }
+
+ public class Query
+ {
+ private readonly List _heroes = new()
+ {
+ new Hero { Id = 1, Name = "R2-D2" },
+ new Hero { Id = 2, Name = "Batman" },
+ new Hero { Id = 3, Name = "Wonder Woman" },
+ new Hero { Id = 4, Name = "Tom Pallister" }
+ };
+
+ [GraphQLMetadata("hero")]
+ public Hero GetHero(int id)
+ {
+ return _heroes.FirstOrDefault(x => x.Id == id);
+ }
+ }
+
+ public class GraphQlDelegatingHandler : DelegatingHandler
+ {
+ //private readonly ISchema _schema;
+ private readonly IDocumentExecuter _executer;
+ private readonly IDocumentWriter _writer;
+
+ public GraphQlDelegatingHandler(IDocumentExecuter executer, IDocumentWriter writer)
+ {
+ _executer = executer;
+ _writer = writer;
+ }
+
+ protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
+ {
+ //try get query from body, could check http method :)
+ var query = await request.Content.ReadAsStringAsync(cancellationToken);
+
+ //if not body try query string, dont hack like this in real world..
+ if (query.Length == 0)
+ {
+ var decoded = WebUtility.UrlDecode(request.RequestUri.Query);
+ query = decoded.Replace("?query=", string.Empty);
+ }
+
+ var result = await _executer.ExecuteAsync(_ =>
+ {
+ _.Query = query;
+ });
+
+ var responseBody = await _writer.WriteToStringAsync(result);
+
+ //maybe check for errors and headers etc in real world?
+ var response = new HttpResponseMessage(HttpStatusCode.OK)
+ {
+ Content = new StringContent(responseBody)
+ };
+
+ //ocelot will treat this like any other http request...
+ return response;
+ }
+ }
+
+ public class Program
+ {
+ public static void Main()
+ {
+ var schema = Schema.For(@"
+ type Hero {
+ id: Int
+ name: String
+ }
+
+ type Query {
+ hero(id: Int): Hero
+ }
+ ", _ =>
+ {
+ _.Types.Include();
+ });
+
+ new WebHostBuilder()
+ .UseKestrel()
+ .UseContentRoot(Directory.GetCurrentDirectory())
+ .ConfigureAppConfiguration((hostingContext, config) =>
+ {
+ config
+ .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
+ .AddJsonFile("appsettings.json", true, true)
+ .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
+ .AddJsonFile("ocelot.json", false, false)
+ .AddEnvironmentVariables();
+ })
+ .ConfigureServices(s =>
+ {
+ s.AddSingleton(schema);
+ s.AddOcelot()
+ .AddDelegatingHandler();
+ })
+ .ConfigureLogging((hostingContext, logging) =>
+ {
+ logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
+ logging.AddConsole();
+ })
+ .UseIISIntegration()
+ .Configure(app =>
+ {
+ app.UseOcelot().Wait();
+ })
+ .Build()
+ .Run();
+ }
+ }
+}
diff --git a/samples/OcelotGraphQL/README.md b/samples/OcelotGraphQL/README.md
index b2101d568..7f16ed985 100644
--- a/samples/OcelotGraphQL/README.md
+++ b/samples/OcelotGraphQL/README.md
@@ -1,71 +1,71 @@
-# Ocelot using GraphQL example
-
-Loads of people keep asking me if Ocelot will every support GraphQL, in my mind Ocelot and GraphQL are two different things that can work together.
-I would not try and implement GraphQL in Ocelot instead I would either have Ocelot in front of GraphQL to handle things like authorisation / authentication or I would
-bring in the awesome [graphql-dotnet](https://github.com/graphql-dotnet/graphql-dotnet) library and use it in a [DelegatingHandler](http://ocelot.readthedocs.io/en/latest/features/delegatinghandlers.html). This way you could have Ocelot and GraphQL without the extra hop to GraphQL. This same is an example of how to do that.
-
-## Example
-
-If you run this project with
-
-$ dotnet run
-
-Use postman or something to make the following requests and you can see Ocelot and GraphQL in action together...
-
-GET http://localhost:5000/graphql?query={ hero(id: 4) { id name } }
-
-RESPONSE
-```json
- {
- "data": {
- "hero": {
- "id": 4,
- "name": "Tom Pallister"
- }
- }
- }
-```
-
-POST http://localhost:5000/graphql
-
-BODY
-```json
- { hero(id: 4) { id name } }
-```
-
-RESPONSE
-```json
- {
- "data": {
- "hero": {
- "id": 4,
- "name": "Tom Pallister"
- }
- }
- }
-```
-
-## Notes
-
-Please note this project never goes out to another service, it just gets the data for GraphQL in memory. You would need to add the details of your GraphQL server in ocelot.json e.g.
-
-```json
-{
- "ReRoutes": [
- {
- "DownstreamPathTemplate": "/graphql",
- "DownstreamScheme": "http",
- "DownstreamHostAndPorts": [
- {
- "Host": "yourgraphqlhost.com",
- "Port": 80
- }
- ],
- "UpstreamPathTemplate": "/graphql",
- "DelegatingHandlers": [
- "GraphQlDelegatingHandler"
- ]
- }
- ]
- }
+# Ocelot using GraphQL example
+
+Loads of people keep asking me if Ocelot will every support GraphQL, in my mind Ocelot and GraphQL are two different things that can work together.
+I would not try and implement GraphQL in Ocelot instead I would either have Ocelot in front of GraphQL to handle things like authorization / authentication or I would
+bring in the awesome [graphql-dotnet](https://github.com/graphql-dotnet/graphql-dotnet) library and use it in a [DelegatingHandler](http://ocelot.readthedocs.io/en/latest/features/delegatinghandlers.html). This way you could have Ocelot and GraphQL without the extra hop to GraphQL. This same is an example of how to do that.
+
+## Example
+
+If you run this project with
+
+$ dotnet run
+
+Use postman or something to make the following requests and you can see Ocelot and GraphQL in action together...
+
+GET http://localhost:5000/graphql?query={ hero(id: 4) { id name } }
+
+RESPONSE
+```json
+ {
+ "data": {
+ "hero": {
+ "id": 4,
+ "name": "Tom Pallister"
+ }
+ }
+ }
+```
+
+POST http://localhost:5000/graphql
+
+BODY
+```json
+ { hero(id: 4) { id name } }
+```
+
+RESPONSE
+```json
+ {
+ "data": {
+ "hero": {
+ "id": 4,
+ "name": "Tom Pallister"
+ }
+ }
+ }
+```
+
+## Notes
+
+Please note this project never goes out to another service, it just gets the data for GraphQL in memory. You would need to add the details of your GraphQL server in ocelot.json e.g.
+
+```json
+{
+ "Routes": [
+ {
+ "DownstreamPathTemplate": "/graphql",
+ "DownstreamScheme": "http",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "yourgraphqlhost.com",
+ "Port": 80
+ }
+ ],
+ "UpstreamPathTemplate": "/graphql",
+ "DelegatingHandlers": [
+ "GraphQlDelegatingHandler"
+ ]
+ }
+ ]
+ }
```
\ No newline at end of file
diff --git a/samples/OcelotGraphQL/ocelot.json b/samples/OcelotGraphQL/ocelot.json
index 115006f9f..c716bf258 100644
--- a/samples/OcelotGraphQL/ocelot.json
+++ b/samples/OcelotGraphQL/ocelot.json
@@ -1,19 +1,19 @@
-{
- "ReRoutes": [
- {
- "DownstreamPathTemplate": "/",
- "DownstreamScheme": "http",
- "DownstreamHostAndPorts": [
- {
- "Host": "jsonplaceholder.typicode.com",
- "Port": 80
- }
- ],
- "UpstreamPathTemplate": "/graphql",
- "DelegatingHandlers": [
- "GraphQlDelegatingHandler"
- ]
- }
- ]
- }
+{
+ "Routes": [
+ {
+ "DownstreamPathTemplate": "/",
+ "DownstreamScheme": "http",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "jsonplaceholder.typicode.com",
+ "Port": 80
+ }
+ ],
+ "UpstreamPathTemplate": "/graphql",
+ "DelegatingHandlers": [
+ "GraphQlDelegatingHandler"
+ ]
+ }
+ ]
+ }
\ No newline at end of file
diff --git a/samples/OcelotKube/ApiGateway/ApiGateway.csproj b/samples/OcelotKube/ApiGateway/ApiGateway.csproj
deleted file mode 100644
index 05f462ab7..000000000
--- a/samples/OcelotKube/ApiGateway/ApiGateway.csproj
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
- netcoreapp3.1
- InProcess
- Linux
-
-
-
-
-
-
-
-
-
-
-
diff --git a/samples/OcelotKube/ApiGateway/Dockerfile b/samples/OcelotKube/ApiGateway/Dockerfile
index 1e3fcd116..990742e4a 100644
--- a/samples/OcelotKube/ApiGateway/Dockerfile
+++ b/samples/OcelotKube/ApiGateway/Dockerfile
@@ -1,19 +1,20 @@
-FROM mcr.microsoft.com/dotnet/core/aspnet:2.1-stretch-slim AS base
+FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
WORKDIR /app
EXPOSE 80
+EXPOSE 443
-FROM mcr.microsoft.com/dotnet/core/sdk:2.1-stretch AS build
+FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
-COPY ["ApiGateway/ApiGateway.csproj", "ApiGateway/"]
-RUN dotnet restore "ApiGateway/ApiGateway.csproj"
+COPY ["ApiGateway/Ocelot.Samples.OcelotKube.ApiGateway.csproj", "ApiGateway/"]
+RUN dotnet restore "ApiGateway/Ocelot.Samples.OcelotKube.ApiGateway.csproj"
COPY . .
WORKDIR "/src/ApiGateway"
-RUN dotnet build "ApiGateway.csproj" -c Release -o /app
+RUN dotnet build "Ocelot.Samples.OcelotKube.ApiGateway.csproj" -c Release -o /app/build
FROM build AS publish
-RUN dotnet publish "ApiGateway.csproj" -c Release -o /app
+RUN dotnet publish "Ocelot.Samples.OcelotKube.ApiGateway.csproj" -c Release -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
-COPY --from=publish /app .
-ENTRYPOINT ["dotnet", "ApiGateway.dll"]
\ No newline at end of file
+COPY --from=publish /app/publish .
+ENTRYPOINT ["dotnet", "Ocelot.Samples.OcelotKube.ApiGateway.dll"]
diff --git a/samples/OcelotKube/ApiGateway/Ocelot.Samples.OcelotKube.ApiGateway.csproj b/samples/OcelotKube/ApiGateway/Ocelot.Samples.OcelotKube.ApiGateway.csproj
new file mode 100644
index 000000000..5d08a3b8a
--- /dev/null
+++ b/samples/OcelotKube/ApiGateway/Ocelot.Samples.OcelotKube.ApiGateway.csproj
@@ -0,0 +1,18 @@
+
+
+
+ net7.0
+ InProcess
+ Linux
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/OcelotKube/ApiGateway/Program.cs b/samples/OcelotKube/ApiGateway/Program.cs
index 978c8ccc9..36f5fe49c 100644
--- a/samples/OcelotKube/ApiGateway/Program.cs
+++ b/samples/OcelotKube/ApiGateway/Program.cs
@@ -1,32 +1,27 @@
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
-using Ocelot.DependencyInjection;
-using Ocelot.Middleware;
-using Ocelot.Provider.Kubernetes;
-namespace ApiGateway
+namespace Ocelot.Samples.OcelotKube.ApiGateway;
+
+public class Program
{
- public class Program
+ public static void Main(string[] args)
{
- public static void Main(string[] args)
- {
- BuildWebHost(args).Run();
- }
-
- public static IWebHost BuildWebHost(string[] args) =>
- WebHost.CreateDefaultBuilder(args)
- .ConfigureAppConfiguration((hostingContext, config) =>
- {
- config
- .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
- .AddJsonFile("appsettings.json", true, true)
- .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
- .AddJsonFile("ocelot.json", false, false)
- .AddEnvironmentVariables();
- })
- .UseStartup()
- .Build();
+ BuildWebHost(args).Run();
}
-}
+ public static IWebHost BuildWebHost(string[] args) =>
+ WebHost.CreateDefaultBuilder(args)
+ .ConfigureAppConfiguration((hostingContext, config) =>
+ {
+ config
+ .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
+ .AddJsonFile("appsettings.json", true, true)
+ .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
+ .AddJsonFile("ocelot.json", false, false)
+ .AddEnvironmentVariables();
+ })
+ .UseStartup()
+ .Build();
+}
diff --git a/samples/OcelotKube/ApiGateway/Properties/launchSettings.json b/samples/OcelotKube/ApiGateway/Properties/launchSettings.json
index 5315ab2c8..5684b7657 100644
--- a/samples/OcelotKube/ApiGateway/Properties/launchSettings.json
+++ b/samples/OcelotKube/ApiGateway/Properties/launchSettings.json
@@ -11,6 +11,7 @@
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
+ "launchUrl": "values",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
@@ -18,6 +19,7 @@
"ApiGateway": {
"commandName": "Project",
"launchBrowser": true,
+ "launchUrl": "values",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
@@ -26,7 +28,7 @@
"Docker": {
"commandName": "Docker",
"launchBrowser": true,
- "launchUrl": "{Scheme}://localhost:{ServicePort}"
+ "launchUrl": "{Scheme}://localhost:{ServicePort}/values"
}
}
-}
\ No newline at end of file
+}
diff --git a/samples/OcelotKube/ApiGateway/Startup.cs b/samples/OcelotKube/ApiGateway/Startup.cs
index d7b2473c4..2afcde95c 100644
--- a/samples/OcelotKube/ApiGateway/Startup.cs
+++ b/samples/OcelotKube/ApiGateway/Startup.cs
@@ -1,31 +1,32 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using Ocelot.Provider.Kubernetes;
-namespace ApiGateway
+namespace Ocelot.Samples.OcelotKube.ApiGateway;
+
+public class Startup
{
- public class Startup
+ // This method gets called by the runtime. Use this method to add services to the container.
+ // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
+ public void ConfigureServices(IServiceCollection services)
{
- // This method gets called by the runtime. Use this method to add services to the container.
- // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddOcelot()
- .AddKubernetes();
- }
+ services.AddOcelot()
+ .AddKubernetes();
+ }
- // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
- public void Configure(IApplicationBuilder app, IHostingEnvironment env)
+ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+ {
+ if (env.IsDevelopment())
{
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- }
-
- app.UseOcelot().Wait();
+ app.UseDeveloperExceptionPage();
}
+
+ app.UseOcelot().Wait();
}
}
diff --git a/samples/OcelotKube/ApiGateway/ocelot.json b/samples/OcelotKube/ApiGateway/ocelot.json
index ec70503e9..6a28b9eec 100644
--- a/samples/OcelotKube/ApiGateway/ocelot.json
+++ b/samples/OcelotKube/ApiGateway/ocelot.json
@@ -1,20 +1,20 @@
-{
- "ReRoutes": [
- {
- "DownstreamPathTemplate": "/api/values",
- "DownstreamScheme": "http",
- "UpstreamPathTemplate": "/values",
- "ServiceName": "downstreamservice",
- "UpstreamHttpMethod": [ "Get" ]
- }
- ],
- "GlobalConfiguration": {
- "ServiceDiscoveryProvider": {
- "Host": "192.168.0.13",
- "Port": 443,
- "Token": "txpc696iUhbVoudg164r93CxDTrKRVWG",
- "Namespace": "dev",
- "Type": "kube"
- }
- }
-}
+{
+ "Routes": [
+ {
+ "DownstreamPathTemplate": "/api/values",
+ "DownstreamScheme": "http",
+ "UpstreamPathTemplate": "/values",
+ "ServiceName": "downstreamservice",
+ "UpstreamHttpMethod": [ "Get" ]
+ }
+ ],
+ "GlobalConfiguration": {
+ "ServiceDiscoveryProvider": {
+ "Host": "192.168.0.13",
+ "Port": 443,
+ "Token": "txpc696iUhbVoudg164r93CxDTrKRVWG",
+ "Namespace": "dev",
+ "Type": "kube"
+ }
+ }
+}
diff --git a/samples/OcelotKube/DownstreamService/Controllers/ValuesController.cs b/samples/OcelotKube/DownstreamService/Controllers/ValuesController.cs
index 425cf18de..48ebcb7d2 100644
--- a/samples/OcelotKube/DownstreamService/Controllers/ValuesController.cs
+++ b/samples/OcelotKube/DownstreamService/Controllers/ValuesController.cs
@@ -1,45 +1,41 @@
-using System;
+using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Mvc;
-namespace DownstreamService.Controllers
+namespace Ocelot.Samples.OcelotKube.DownstreamService.Controllers;
+
+[ApiController]
+[Route("api/[controller]")]
+public class ValuesController : ControllerBase
{
- [Route("api/[controller]")]
- [ApiController]
- public class ValuesController : ControllerBase
+ // GET api/values
+ [HttpGet]
+ public ActionResult> Get()
{
- // GET api/values
- [HttpGet]
- public ActionResult> Get()
- {
- return new string[] { "value1", "value2" };
- }
+ return new[] { "value1", "value2" };
+ }
- // GET api/values/5
- [HttpGet("{id}")]
- public ActionResult Get(int id)
- {
- return "value";
- }
+ // GET api/values/5
+ [HttpGet("{id}")]
+ public ActionResult Get(int id)
+ {
+ return "value";
+ }
- // POST api/values
- [HttpPost]
- public void Post([FromBody] string value)
- {
- }
+ // POST api/values
+ [HttpPost]
+ public void Post([FromBody] string value)
+ {
+ }
- // PUT api/values/5
- [HttpPut("{id}")]
- public void Put(int id, [FromBody] string value)
- {
- }
+ // PUT api/values/5
+ [HttpPut("{id}")]
+ public void Put(int id, [FromBody] string value)
+ {
+ }
- // DELETE api/values/5
- [HttpDelete("{id}")]
- public void Delete(int id)
- {
- }
+ // DELETE api/values/5
+ [HttpDelete("{id}")]
+ public void Delete(int id)
+ {
}
}
diff --git a/samples/OcelotKube/DownstreamService/Controllers/WeatherForecastController.cs b/samples/OcelotKube/DownstreamService/Controllers/WeatherForecastController.cs
new file mode 100644
index 000000000..33222b1f1
--- /dev/null
+++ b/samples/OcelotKube/DownstreamService/Controllers/WeatherForecastController.cs
@@ -0,0 +1,39 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Ocelot.Samples.OcelotKube.DownstreamService.Controllers;
+
+using Models;
+
+[ApiController]
+[Route("api/[controller]")]
+public class WeatherForecastController : ControllerBase
+{
+ private static readonly string[] Summaries = new[]
+ {
+ "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
+ };
+
+ private readonly ILogger _logger;
+
+ public WeatherForecastController(ILogger logger)
+ {
+ _logger = logger;
+ }
+
+ [HttpGet(Name = "GetWeatherForecast")]
+ public IEnumerable Get()
+ {
+ return Enumerable.Range(1, 5)
+ .Select(index => new WeatherForecast
+ {
+ Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
+ TemperatureC = Random.Shared.Next(-20, 55),
+ Summary = Summaries[Random.Shared.Next(Summaries.Length)]
+ })
+ .ToArray();
+ }
+}
diff --git a/samples/OcelotKube/DownstreamService/Dockerfile b/samples/OcelotKube/DownstreamService/Dockerfile
index a96955150..985096fcc 100644
--- a/samples/OcelotKube/DownstreamService/Dockerfile
+++ b/samples/OcelotKube/DownstreamService/Dockerfile
@@ -1,19 +1,20 @@
-FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
+FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
WORKDIR /app
EXPOSE 80
+EXPOSE 443
-FROM microsoft/dotnet:2.1-sdk AS build
+FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
-COPY ["DownstreamService/DownstreamService.csproj", "DownstreamService/"]
-RUN dotnet restore "DownstreamService/DownstreamService.csproj"
+COPY ["DownstreamService/Ocelot.Samples.OcelotKube.DownstreamService.csproj", "DownstreamService/"]
+RUN dotnet restore "DownstreamService/Ocelot.Samples.OcelotKube.DownstreamService.csproj"
COPY . .
WORKDIR "/src/DownstreamService"
-RUN dotnet build "DownstreamService.csproj" -c Release -o /app
+RUN dotnet build "Ocelot.Samples.OcelotKube.DownstreamService.csproj" -c Release -o /app/build
FROM build AS publish
-RUN dotnet publish "DownstreamService.csproj" -c Release -o /app
+RUN dotnet publish "Ocelot.Samples.OcelotKube.DownstreamService.csproj" -c Release -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
-COPY --from=publish /app .
-ENTRYPOINT ["dotnet", "DownstreamService.dll"]
+COPY --from=publish /app/publish .
+ENTRYPOINT ["dotnet", "Ocelot.Samples.OcelotKube.DownstreamService.dll"]
diff --git a/samples/OcelotKube/DownstreamService/Dockerfile.develop b/samples/OcelotKube/DownstreamService/Dockerfile.develop
deleted file mode 100644
index 6f49a2909..000000000
--- a/samples/OcelotKube/DownstreamService/Dockerfile.develop
+++ /dev/null
@@ -1,15 +0,0 @@
-FROM microsoft/dotnet:2.1-sdk
-ARG BUILD_CONFIGURATION=Debug
-ENV ASPNETCORE_ENVIRONMENT=Development
-ENV DOTNET_USE_POLLING_FILE_WATCHER=true
-EXPOSE 80
-
-WORKDIR /src
-COPY ["DownstreamService/DownstreamService.csproj", "DownstreamService/"]
-
-RUN dotnet restore "DownstreamService/DownstreamService.csproj"
-COPY . .
-WORKDIR "/src/DownstreamService"
-RUN dotnet build --no-restore "DownstreamService.csproj" -c $BUILD_CONFIGURATION
-
-ENTRYPOINT ["dotnet", "run", "--no-build", "--no-launch-profile", "-c", "$BUILD_CONFIGURATION", "--"]
\ No newline at end of file
diff --git a/samples/OcelotKube/DownstreamService/Models/WeatherForecast.cs b/samples/OcelotKube/DownstreamService/Models/WeatherForecast.cs
new file mode 100644
index 000000000..66e01aee6
--- /dev/null
+++ b/samples/OcelotKube/DownstreamService/Models/WeatherForecast.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace Ocelot.Samples.OcelotKube.DownstreamService.Models;
+
+public class WeatherForecast
+{
+ public DateOnly Date { get; set; }
+
+ public int TemperatureC { get; set; }
+
+ public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
+
+ public string Summary { get; set; }
+}
diff --git a/samples/OcelotKube/DownstreamService/DownstreamService.csproj b/samples/OcelotKube/DownstreamService/Ocelot.Samples.OcelotKube.DownstreamService.csproj
similarity index 52%
rename from samples/OcelotKube/DownstreamService/DownstreamService.csproj
rename to samples/OcelotKube/DownstreamService/Ocelot.Samples.OcelotKube.DownstreamService.csproj
index d430c524e..56b2d53e7 100644
--- a/samples/OcelotKube/DownstreamService/DownstreamService.csproj
+++ b/samples/OcelotKube/DownstreamService/Ocelot.Samples.OcelotKube.DownstreamService.csproj
@@ -1,15 +1,14 @@
-
-
-
- netcoreapp3.1
- InProcess
- Linux
-
-
-
-
-
-
-
-
-
+
+
+
+ net7.0
+ InProcess
+ Linux
+
+
+
+
+
+
+
+
diff --git a/samples/OcelotKube/DownstreamService/Program.cs b/samples/OcelotKube/DownstreamService/Program.cs
index 03e1b8aea..94f139c01 100644
--- a/samples/OcelotKube/DownstreamService/Program.cs
+++ b/samples/OcelotKube/DownstreamService/Program.cs
@@ -1,24 +1,57 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using System.Text.Json;
+using System.Text.Json.Serialization;
-namespace DownstreamService
+namespace Ocelot.Samples.OcelotKube.DownstreamService;
+
+public class Program
{
- public class Program
+ public static void Main(string[] args)
{
- public static void Main(string[] args)
+ var builder = WebApplication.CreateBuilder(args);
+
+ builder.Services
+ // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
+ .AddEndpointsApiExplorer()
+ .AddSwaggerGen()
+
+ .AddControllers()
+ .AddJsonOptions(options =>
+ {
+ options.AllowInputFormatterExceptionMessages = true;
+
+ var jOptions = options.JsonSerializerOptions;
+ jOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase, true));
+ jOptions.PropertyNameCaseInsensitive = true;
+ jOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
+ });
+
+ AddApplicationServices(builder.Services);
+
+ var app = builder.Build();
+
+ // Configure the HTTP request pipeline.
+ if (app.Environment.IsDevelopment())
{
- CreateWebHostBuilder(args).Build().Run();
+ app.UseSwagger();
+ app.UseSwaggerUI();
}
- public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
- WebHost.CreateDefaultBuilder(args)
- .UseStartup();
+ app.UseHttpsRedirection();
+ app.UseAuthorization();
+
+ app.MapControllers();
+ app.Run();
+ }
+
+ private static void AddApplicationServices(IServiceCollection services)
+ {
+ services.AddHttpClient(); // to keep performance of HTTP Client high
+ //services.AddSingleton
+ //services.AddScoped
+ //services.AddTransient
}
}
diff --git a/samples/OcelotKube/DownstreamService/Properties/launchSettings.json b/samples/OcelotKube/DownstreamService/Properties/launchSettings.json
index 30d6118c6..6f9313866 100644
--- a/samples/OcelotKube/DownstreamService/Properties/launchSettings.json
+++ b/samples/OcelotKube/DownstreamService/Properties/launchSettings.json
@@ -12,7 +12,7 @@
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
- "launchUrl": "api/values",
+ "launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
@@ -20,7 +20,7 @@
"DownstreamService": {
"commandName": "Project",
"launchBrowser": true,
- "launchUrl": "api/values",
+ "launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
@@ -29,7 +29,7 @@
"Docker": {
"commandName": "Docker",
"launchBrowser": true,
- "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/api/values"
+ "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger"
}
}
-}
\ No newline at end of file
+}
diff --git a/samples/OcelotKube/DownstreamService/Startup.cs b/samples/OcelotKube/DownstreamService/Startup.cs
deleted file mode 100644
index 9a927a37b..000000000
--- a/samples/OcelotKube/DownstreamService/Startup.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-
-namespace DownstreamService
-{
- public class Startup
- {
- public Startup(IConfiguration configuration)
- {
- Configuration = configuration;
- }
-
- public IConfiguration Configuration { get; }
-
- // This method gets called by the runtime. Use this method to add services to the container.
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
- }
-
- // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
- public void Configure(IApplicationBuilder app, IHostingEnvironment env)
- {
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- }
-
- app.UseMvc();
- }
- }
-}
diff --git a/samples/OcelotOpenTracing/OcelotOpenTracing.csproj b/samples/OcelotOpenTracing/OcelotOpenTracing.csproj
new file mode 100644
index 000000000..0384c8c01
--- /dev/null
+++ b/samples/OcelotOpenTracing/OcelotOpenTracing.csproj
@@ -0,0 +1,30 @@
+
+
+
+ Exe
+ net7.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+
+
diff --git a/samples/OcelotOpenTracing/Program.cs b/samples/OcelotOpenTracing/Program.cs
new file mode 100644
index 000000000..0c472ac23
--- /dev/null
+++ b/samples/OcelotOpenTracing/Program.cs
@@ -0,0 +1,68 @@
+using System.IO;
+
+using Jaeger;
+
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+
+using Ocelot.DependencyInjection;
+using Ocelot.Middleware;
+using Ocelot.Tracing.OpenTracing;
+
+using OpenTracing.Util;
+
+namespace OcelotOpenTracing
+{
+ internal static class Program
+ {
+ private static void Main(string[] args)
+ {
+ Host.CreateDefaultBuilder()
+ .ConfigureWebHostDefaults(webBuilder =>
+ {
+ webBuilder
+ .UseContentRoot(Directory.GetCurrentDirectory())
+ .UseKestrel()
+ .ConfigureAppConfiguration((hostingContext, config) =>
+ {
+ config
+ .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
+ .AddJsonFile("appsettings.json", optional: false, reloadOnChange: false)
+ .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json",
+ optional: true, reloadOnChange: false)
+ .AddJsonFile("ocelot.json", optional: false, reloadOnChange: true)
+ .AddEnvironmentVariables();
+ })
+ .ConfigureServices((context, services) =>
+ {
+ services.AddSingleton(sp =>
+ {
+ var loggerFactory = sp.GetService();
+ var config = new Configuration(context.HostingEnvironment.ApplicationName, loggerFactory);
+
+ var tracer = config.GetTracer();
+ GlobalTracer.Register(tracer);
+ return tracer;
+ });
+
+ services
+ .AddOcelot()
+ .AddOpenTracing();
+ })
+ .ConfigureLogging(logging =>
+ {
+ logging.AddConsole();
+ })
+ .Configure(app =>
+ {
+ app.UseOcelot().Wait();
+ });
+ })
+ .Build()
+ .Run();
+ }
+ }
+}
diff --git a/samples/OcelotOpenTracing/appsettings.Development.json b/samples/OcelotOpenTracing/appsettings.Development.json
new file mode 100644
index 000000000..8983e0fc1
--- /dev/null
+++ b/samples/OcelotOpenTracing/appsettings.Development.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ }
+}
diff --git a/samples/OcelotOpenTracing/appsettings.json b/samples/OcelotOpenTracing/appsettings.json
new file mode 100644
index 000000000..d9d9a9bff
--- /dev/null
+++ b/samples/OcelotOpenTracing/appsettings.json
@@ -0,0 +1,10 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/samples/OcelotOpenTracing/ocelot.json b/samples/OcelotOpenTracing/ocelot.json
new file mode 100644
index 000000000..a5a670321
--- /dev/null
+++ b/samples/OcelotOpenTracing/ocelot.json
@@ -0,0 +1,24 @@
+{
+ "ReRoutes": [
+ {
+ "HttpHandlerOptions": {
+ "UseTracing": true
+ },
+ "DownstreamPathTemplate": "/todos/{id}",
+ "DownstreamScheme": "https",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "jsonplaceholder.typicode.com",
+ "Port": 443
+ }
+ ],
+ "UpstreamPathTemplate": "/posts/{id}",
+ "UpstreamHttpMethod": [
+ "Get"
+ ]
+ }
+ ],
+ "GlobalConfiguration": {
+ "BaseUrl": "https://localhost:5000"
+ }
+}
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.cs b/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.cs
index 28ea7abbb..a017ee028 100644
--- a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.cs
+++ b/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.cs
@@ -3,13 +3,14 @@
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------
+using System.Collections.Generic;
+using System.Fabric;
+
+using Microsoft.ServiceFabric.Services.Communication.Runtime;
+using Microsoft.ServiceFabric.Services.Runtime;
+
namespace OcelotApplicationApiGateway
{
- using System.Fabric;
- using Microsoft.ServiceFabric.Services.Communication.Runtime;
- using Microsoft.ServiceFabric.Services.Runtime;
- using System.Collections.Generic;
-
/// Service that handles front-end web requests and acts as a proxy to the back-end data for the UI web page.
/// It is a stateless service that hosts a Web API application on OWIN.
internal sealed class OcelotServiceWebService : StatelessService
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.csproj b/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.csproj
index 186b43f36..416ea124b 100644
--- a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.csproj
+++ b/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.csproj
@@ -1,11 +1,10 @@
-
+
Stateless Web Service for Stateful OcelotApplicationApiGateway App
-
- netcoreapp3.1
+ net7.0
OcelotApplicationApiGateway
- Exe
OcelotApplicationApiGateway
+ x64
@@ -13,10 +12,10 @@
-
-
+
+
-
\ No newline at end of file
+
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/Program.cs b/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/Program.cs
index 7da30a50a..9a4dd524e 100644
--- a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/Program.cs
+++ b/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/Program.cs
@@ -3,16 +3,15 @@
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------
-namespace OcelotApplicationApiGateway
+using System;
+using System.Diagnostics.Tracing;
+using System.Threading;
-{
- using System;
- using System.Fabric;
- using System.Threading;
- using Microsoft.ServiceFabric.Services.Runtime;
- using System.Diagnostics.Tracing;
+using Microsoft.ServiceFabric.Services.Runtime;
+namespace OcelotApplicationApiGateway
+{
///
/// The service host is the executable that hosts the Service instances.
///
@@ -25,7 +24,7 @@ public static void Main(string[] args)
{
//Creating a new event listener to redirect the traces to a file
- ServiceEventListener listener = new ServiceEventListener("OcelotApplicationApiGateway");
+ var listener = new ServiceEventListener("OcelotApplicationApiGateway");
listener.EnableEvents(ServiceEventSource.Current, EventLevel.LogAlways, EventKeywords.All);
// The ServiceManifest.XML file defines one or more service type names.
@@ -33,7 +32,7 @@ public static void Main(string[] args)
// When Service Fabric creates an instance of this service type,
// an instance of the class is created in this host process.
ServiceRuntime
- .RegisterServiceAsync("OcelotApplicationApiGatewayType", context => new OcelotServiceWebService (context))
+ .RegisterServiceAsync("OcelotApplicationApiGatewayType", context => new OcelotServiceWebService(context))
.GetAwaiter()
.GetResult();
@@ -44,8 +43,8 @@ public static void Main(string[] args)
catch (Exception ex)
{
ServiceEventSource.Current.ServiceHostInitializationFailed(ex);
- throw ex;
+ throw;
}
}
}
-}
\ No newline at end of file
+}
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/ServiceEventListener.cs b/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/ServiceEventListener.cs
index 555f9fde8..d6d64dac2 100644
--- a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/ServiceEventListener.cs
+++ b/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/ServiceEventListener.cs
@@ -3,19 +3,15 @@
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------
+using System;
+using System.Diagnostics.Tracing;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
+
namespace OcelotApplicationApiGateway
{
- using System;
- using System.IO;
- using System.Linq;
- using System.Diagnostics.Tracing;
- using System.Fabric;
- using System.Fabric.Common;
- using System.Fabric.Common.Tracing;
- using Microsoft.ServiceFabric.Services.Runtime;
- using System.Globalization;
- using System.Text;
-
///
/// ServiceEventListener is a class which listens to the eventsources registered and redirects the traces to a file
/// Note that this class serves as a template to EventListener class and redirects the logs to /tmp/{appnameyyyyMMddHHmmssffff}.
@@ -24,12 +20,12 @@ namespace OcelotApplicationApiGateway
///
internal class ServiceEventListener : EventListener
{
- private string fileName;
- private string filepath = Path.GetTempPath();
+ private readonly string _fileName;
+ private readonly string _filepath = Path.GetTempPath();
public ServiceEventListener(string appName)
{
- this.fileName = appName + DateTime.Now.ToString("yyyyMMddHHmmssffff");
+ _fileName = appName + DateTime.Now.ToString("yyyyMMddHHmmssffff");
}
///
@@ -38,10 +34,10 @@ public ServiceEventListener(string appName)
/// The event arguments that describe the event.
protected override void OnEventWritten(EventWrittenEventArgs eventData)
{
- using (StreamWriter writer = new StreamWriter( new FileStream(filepath + fileName, FileMode.Append)))
+ using (var writer = new StreamWriter(new FileStream(_filepath + _fileName, FileMode.Append)))
{
// report all event information
- writer.Write(" {0} ", Write(eventData.Task.ToString(),
+ writer.Write(" {0} ", Write(eventData.Task.ToString(),
eventData.EventName,
eventData.EventId.ToString(),
eventData.Level));
@@ -55,9 +51,9 @@ protected override void OnEventWritten(EventWrittenEventArgs eventData)
private static String Write(string taskName, string eventName, string id, EventLevel level)
{
- StringBuilder output = new StringBuilder();
+ var output = new StringBuilder();
- DateTime now = DateTime.UtcNow;
+ var now = DateTime.UtcNow;
output.Append(now.ToString("yyyy/MM/dd-HH:mm:ss.fff", CultureInfo.InvariantCulture));
output.Append(',');
output.Append(ConvertLevelToString(level));
@@ -91,4 +87,4 @@ private static string ConvertLevelToString(EventLevel level)
}
}
}
-}
\ No newline at end of file
+}
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/ServiceEventSource.cs b/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/ServiceEventSource.cs
index c05799556..f880f6af0 100644
--- a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/ServiceEventSource.cs
+++ b/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/ServiceEventSource.cs
@@ -3,19 +3,17 @@
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------
+using System;
+using System.Diagnostics.Tracing;
+
namespace OcelotApplicationApiGateway
{
- using System;
- using System.Diagnostics.Tracing;
- using System.Fabric;
- using Microsoft.ServiceFabric.Services.Runtime;
-
///
/// Implements methods for logging service related events.
///
public class ServiceEventSource : EventSource
{
- public static ServiceEventSource Current = new ServiceEventSource();
+ public static ServiceEventSource Current = new();
// Define an instance method for each event you want to record and apply an [Event] attribute to it.
// The method name is the name of the event.
@@ -28,10 +26,10 @@ public class ServiceEventSource : EventSource
[NonEvent]
public void Message(string message, params object[] args)
{
- if (this.IsEnabled())
+ if (IsEnabled())
{
var finalMessage = string.Format(message, args);
- this.Message(finalMessage);
+ Message(finalMessage);
}
}
@@ -40,9 +38,9 @@ public void Message(string message, params object[] args)
[Event(MessageEventId, Level = EventLevel.Informational, Message = "{0}")]
public void Message(string message)
{
- if (this.IsEnabled())
+ if (IsEnabled())
{
- this.WriteEvent(MessageEventId, message);
+ WriteEvent(MessageEventId, message);
}
}
@@ -51,13 +49,13 @@ public void Message(string message)
[Event(ServiceTypeRegisteredEventId, Level = EventLevel.Informational, Message = "Service host process {0} registered service type {1}")]
public void ServiceTypeRegistered(int hostProcessId, string serviceType)
{
- this.WriteEvent(ServiceTypeRegisteredEventId, hostProcessId, serviceType);
+ WriteEvent(ServiceTypeRegisteredEventId, hostProcessId, serviceType);
}
[NonEvent]
public void ServiceHostInitializationFailed(Exception e)
{
- this.ServiceHostInitializationFailed(e.ToString());
+ ServiceHostInitializationFailed(e.ToString());
}
private const int ServiceHostInitializationFailedEventId = 4;
@@ -65,13 +63,13 @@ public void ServiceHostInitializationFailed(Exception e)
[Event(ServiceHostInitializationFailedEventId, Level = EventLevel.Error, Message = "Service host initialization failed: {0}")]
private void ServiceHostInitializationFailed(string exception)
{
- this.WriteEvent(ServiceHostInitializationFailedEventId, exception);
+ WriteEvent(ServiceHostInitializationFailedEventId, exception);
}
[NonEvent]
public void ServiceWebHostBuilderFailed(Exception e)
{
- this.ServiceWebHostBuilderFailed(e.ToString());
+ ServiceWebHostBuilderFailed(e.ToString());
}
private const int ServiceWebHostBuilderFailedEventId = 5;
@@ -79,7 +77,7 @@ public void ServiceWebHostBuilderFailed(Exception e)
[Event(ServiceWebHostBuilderFailedEventId, Level = EventLevel.Error, Message = "Service Owin Web Host Builder Failed: {0}")]
private void ServiceWebHostBuilderFailed(string exception)
{
- this.WriteEvent(ServiceWebHostBuilderFailedEventId, exception);
+ WriteEvent(ServiceWebHostBuilderFailedEventId, exception);
}
}
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/WebCommunicationListener.cs b/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/WebCommunicationListener.cs
index c1f4a1730..18a80a34f 100644
--- a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/WebCommunicationListener.cs
+++ b/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/WebCommunicationListener.cs
@@ -3,63 +3,62 @@
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------
+using System;
+using System.Fabric;
+using System.Globalization;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+using Microsoft.ServiceFabric.Services.Communication.Runtime;
+
+using Ocelot.DependencyInjection;
+using Ocelot.Middleware;
+
namespace OcelotApplicationApiGateway
{
- using System;
- using System.Fabric;
- using System.Fabric.Description;
- using System.Globalization;
- using System.Threading;
- using System.Threading.Tasks;
- using Microsoft.AspNetCore.Hosting;
- using Microsoft.ServiceFabric.Services.Communication.Runtime;
- using Microsoft.Extensions.Logging;
- using Microsoft.Extensions.Configuration;
- using Microsoft.AspNetCore.Builder;
- using Microsoft.Extensions.DependencyInjection;
- using Ocelot.DependencyInjection;
- using Ocelot.Middleware;
-
public class WebCommunicationListener : ICommunicationListener
{
- private readonly string appRoot;
- private readonly ServiceContext serviceInitializationParameters;
- private string listeningAddress;
- private string publishAddress;
+ private readonly string _appRoot;
+ private readonly ServiceContext _serviceInitializationParameters;
+ private string _listeningAddress;
+ private string _publishAddress;
// OWIN server handle.
- private IWebHost webHost;
+ private IWebHost _webHost;
public WebCommunicationListener(string appRoot, ServiceContext serviceInitializationParameters)
{
- this.appRoot = appRoot;
- this.serviceInitializationParameters = serviceInitializationParameters;
+ _appRoot = appRoot;
+ _serviceInitializationParameters = serviceInitializationParameters;
}
public Task OpenAsync(CancellationToken cancellationToken)
{
ServiceEventSource.Current.Message("Initialize");
- EndpointResourceDescription serviceEndpoint = this.serviceInitializationParameters.CodePackageActivationContext.GetEndpoint("WebEndpoint");
- int port = serviceEndpoint.Port;
+ var serviceEndpoint = _serviceInitializationParameters.CodePackageActivationContext.GetEndpoint("WebEndpoint");
+ var port = serviceEndpoint.Port;
- this.listeningAddress = string.Format(
+ _listeningAddress = string.Format(
CultureInfo.InvariantCulture,
"http://+:{0}/{1}",
port,
- string.IsNullOrWhiteSpace(this.appRoot)
+ string.IsNullOrWhiteSpace(_appRoot)
? string.Empty
- : this.appRoot.TrimEnd('/') + '/');
+ : _appRoot.TrimEnd('/') + '/');
- this.publishAddress = this.listeningAddress.Replace("+", FabricRuntime.GetNodeContext().IPAddressOrFQDN);
+ _publishAddress = _listeningAddress.Replace("+", FabricRuntime.GetNodeContext().IPAddressOrFQDN);
- ServiceEventSource.Current.Message("Starting web server on {0}", this.listeningAddress);
+ ServiceEventSource.Current.Message("Starting web server on {0}", _listeningAddress);
try
{
- this.webHost = new WebHostBuilder()
+ _webHost = new WebHostBuilder()
.UseKestrel()
- .UseUrls(this.listeningAddress)
+ .UseUrls(_listeningAddress)
.ConfigureAppConfiguration((hostingContext, config) =>
{
config
@@ -74,33 +73,35 @@ public Task OpenAsync(CancellationToken cancellationToken)
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
})
- .ConfigureServices(s => {
+ .ConfigureServices(s =>
+ {
s.AddOcelot();
})
- .Configure(a => {
- a.UseOcelot().Wait();
+ .Configure(a =>
+ {
+ a.UseOcelot().Wait(cancellationToken);
})
.Build();
- this.webHost.Start();
+ _webHost.Start();
}
catch (Exception ex)
{
ServiceEventSource.Current.ServiceWebHostBuilderFailed(ex);
}
- return Task.FromResult(this.publishAddress);
+ return Task.FromResult(_publishAddress);
}
public Task CloseAsync(CancellationToken cancellationToken)
{
- this.StopAll();
+ StopAll();
return Task.FromResult(true);
}
public void Abort()
{
- this.StopAll();
+ StopAll();
}
///
@@ -110,10 +111,10 @@ private void StopAll()
{
try
{
- if (this.webHost != null)
+ if (_webHost != null)
{
ServiceEventSource.Current.Message("Stopping web server.");
- this.webHost.Dispose();
+ _webHost.Dispose();
}
}
catch (ObjectDisposedException)
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/ocelot.json b/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/ocelot.json
index 8a6792438..b541e95c4 100644
--- a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/ocelot.json
+++ b/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/ocelot.json
@@ -1,21 +1,21 @@
-{
- "ReRoutes": [
- {
- "DownstreamPathTemplate": "/api/values",
- "UpstreamPathTemplate": "/EquipmentInterfaces",
- "UpstreamHttpMethod": [
- "Get"
- ],
- "DownstreamScheme": "http",
- "ServiceName": "OcelotServiceApplication/OcelotApplicationService"
- }
- ],
- "GlobalConfiguration": {
- "RequestIdKey": "OcRequestId",
- "ServiceDiscoveryProvider": {
- "Host": "localhost",
- "Port": 19081,
- "Type": "ServiceFabric"
- }
- }
-}
+{
+ "Routes": [
+ {
+ "DownstreamPathTemplate": "/api/values",
+ "UpstreamPathTemplate": "/EquipmentInterfaces",
+ "UpstreamHttpMethod": [
+ "Get"
+ ],
+ "DownstreamScheme": "http",
+ "ServiceName": "OcelotServiceApplication/OcelotApplicationService"
+ }
+ ],
+ "GlobalConfiguration": {
+ "RequestIdKey": "OcRequestId",
+ "ServiceDiscoveryProvider": {
+ "Host": "localhost",
+ "Port": 19081,
+ "Type": "ServiceFabric"
+ }
+ }
+}
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationService/ApiGateway.cs b/samples/OcelotServiceFabric/src/OcelotApplicationService/ApiGateway.cs
index 8c0908858..e7b7b5f7f 100644
--- a/samples/OcelotServiceFabric/src/OcelotApplicationService/ApiGateway.cs
+++ b/samples/OcelotServiceFabric/src/OcelotApplicationService/ApiGateway.cs
@@ -2,15 +2,13 @@
using System.Collections.Generic;
using System.Fabric;
using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
+
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
using Microsoft.ServiceFabric.Services.Communication.AspNetCore;
using Microsoft.ServiceFabric.Services.Communication.Runtime;
using Microsoft.ServiceFabric.Services.Runtime;
-using Microsoft.Extensions.Logging;
namespace OcelotApplicationService
{
@@ -31,7 +29,7 @@ protected override IEnumerable CreateServiceInstanceLis
{
return new ServiceInstanceListener[]
{
- new ServiceInstanceListener(serviceContext =>
+ new(serviceContext =>
new KestrelCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
{
Console.WriteLine($"Starting Kestrel on {url}");
@@ -41,7 +39,7 @@ protected override IEnumerable CreateServiceInstanceLis
.UseKestrel()
.ConfigureServices(
services => services
- .AddSingleton(serviceContext))
+ .AddSingleton(serviceContext))
.UseContentRoot(Directory.GetCurrentDirectory())
.UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.UseUniqueServiceUrl)
.UseStartup()
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationService/Controllers/ValuesController.cs b/samples/OcelotServiceFabric/src/OcelotApplicationService/Controllers/ValuesController.cs
index 13e569993..b9d4958f3 100644
--- a/samples/OcelotServiceFabric/src/OcelotApplicationService/Controllers/ValuesController.cs
+++ b/samples/OcelotServiceFabric/src/OcelotApplicationService/Controllers/ValuesController.cs
@@ -1,7 +1,5 @@
-using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
+
using Microsoft.AspNetCore.Mvc;
namespace OcelotApplicationService.Controllers
@@ -13,7 +11,7 @@ public class ValuesController : Controller
[HttpGet]
public IEnumerable Get()
{
- return new string[] { "value1", "value2" };
+ return new[] { "value1", "value2" };
}
// GET api/values/5
@@ -25,13 +23,13 @@ public string Get(int id)
// POST api/values
[HttpPost]
- public void Post([FromBody]string value)
+ public void Post([FromBody] string value)
{
}
// PUT api/values/5
[HttpPut("{id}")]
- public void Put(int id, [FromBody]string value)
+ public void Put(int id, [FromBody] string value)
{
}
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationService/OcelotApplicationService.csproj b/samples/OcelotServiceFabric/src/OcelotApplicationService/OcelotApplicationService.csproj
index ea45d7375..f84012617 100644
--- a/samples/OcelotServiceFabric/src/OcelotApplicationService/OcelotApplicationService.csproj
+++ b/samples/OcelotServiceFabric/src/OcelotApplicationService/OcelotApplicationService.csproj
@@ -1,21 +1,18 @@
-
-
- Stateless Service Application
-
- Exe
- netcoreapp3.1
- OcelotApplicationService
- OcelotApplicationService
- $(PackageTargetFallback)
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
+
+ Stateless Service Application
+ net7.0
+ OcelotApplicationService
+ OcelotApplicationService
+ $(PackageTargetFallback)
+ x64
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationService/Program.cs b/samples/OcelotServiceFabric/src/OcelotApplicationService/Program.cs
index b9d7d72e4..4862426a6 100644
--- a/samples/OcelotServiceFabric/src/OcelotApplicationService/Program.cs
+++ b/samples/OcelotServiceFabric/src/OcelotApplicationService/Program.cs
@@ -1,8 +1,8 @@
-using Microsoft.ServiceFabric.Services.Runtime;
using System;
using System.Diagnostics;
using System.Threading;
-using System.Threading.Tasks;
+
+using Microsoft.ServiceFabric.Services.Runtime;
namespace OcelotApplicationService
{
@@ -23,7 +23,7 @@ private static void Main()
ServiceRuntime.RegisterServiceAsync("OcelotApplicationServiceType",
context => new ApiGateway(context)).GetAwaiter().GetResult();
- ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(ApiGateway).Name);
+ ServiceEventSource.Current.ServiceTypeRegistered(Environment.ProcessId, nameof(ApiGateway));
// Prevents this host process from terminating so services keeps running.
Thread.Sleep(Timeout.Infinite);
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationService/Properties/launchSettings.json b/samples/OcelotServiceFabric/src/OcelotApplicationService/Properties/launchSettings.json
new file mode 100644
index 000000000..fb64c02b5
--- /dev/null
+++ b/samples/OcelotServiceFabric/src/OcelotApplicationService/Properties/launchSettings.json
@@ -0,0 +1,12 @@
+{
+ "profiles": {
+ "OcelotApplicationService": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "https://localhost:57625;http://localhost:57626"
+ }
+ }
+}
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationService/ServiceEventSource.cs b/samples/OcelotServiceFabric/src/OcelotApplicationService/ServiceEventSource.cs
index 13e2df12a..f58ddcbcc 100644
--- a/samples/OcelotServiceFabric/src/OcelotApplicationService/ServiceEventSource.cs
+++ b/samples/OcelotServiceFabric/src/OcelotApplicationService/ServiceEventSource.cs
@@ -1,9 +1,6 @@
using System;
-using System.Collections.Generic;
using System.Diagnostics.Tracing;
using System.Fabric;
-using System.Linq;
-using System.Text;
using System.Threading.Tasks;
namespace OcelotApplicationService
@@ -11,7 +8,7 @@ namespace OcelotApplicationService
[EventSource(Name = "MyCompany-ServiceOcelotApplication-OcelotService")]
internal sealed class ServiceEventSource : EventSource
{
- public static readonly ServiceEventSource Current = new ServiceEventSource();
+ public static readonly ServiceEventSource Current = new();
static ServiceEventSource()
{
@@ -46,9 +43,9 @@ public static class Keywords
[NonEvent]
public void Message(string message, params object[] args)
{
- if (this.IsEnabled())
+ if (IsEnabled())
{
- string finalMessage = string.Format(message, args);
+ var finalMessage = string.Format(message, args);
Message(finalMessage);
}
}
@@ -57,7 +54,7 @@ public void Message(string message, params object[] args)
[Event(MessageEventId, Level = EventLevel.Informational, Message = "{0}")]
public void Message(string message)
{
- if (this.IsEnabled())
+ if (IsEnabled())
{
WriteEvent(MessageEventId, message);
}
@@ -66,10 +63,10 @@ public void Message(string message)
[NonEvent]
public void ServiceMessage(ServiceContext serviceContext, string message, params object[] args)
{
- if (this.IsEnabled())
+ if (IsEnabled())
{
- string finalMessage = string.Format(message, args);
+ var finalMessage = string.Format(message, args);
ServiceMessage(
serviceContext.ServiceName.ToString(),
serviceContext.ServiceTypeName,
@@ -157,14 +154,12 @@ public void ServiceRequestStop(string requestTypeName, string exception = "")
#region Private methods
private static long GetReplicaOrInstanceId(ServiceContext context)
{
- StatelessServiceContext stateless = context as StatelessServiceContext;
- if (stateless != null)
+ if (context is StatelessServiceContext stateless)
{
return stateless.InstanceId;
}
- StatefulServiceContext stateful = context as StatefulServiceContext;
- if (stateful != null)
+ if (context is StatefulServiceContext stateful)
{
return stateful.ReplicaId;
}
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationService/Startup.cs b/samples/OcelotServiceFabric/src/OcelotApplicationService/Startup.cs
index 022fcb0ee..53b408e90 100644
--- a/samples/OcelotServiceFabric/src/OcelotApplicationService/Startup.cs
+++ b/samples/OcelotServiceFabric/src/OcelotApplicationService/Startup.cs
@@ -1,13 +1,8 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
+using Microsoft.Extensions.Hosting;
namespace OcelotApplicationService
{
@@ -27,14 +22,12 @@ public void ConfigureServices(IServiceCollection services)
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
- public void Configure(IApplicationBuilder app, IHostingEnvironment env)
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
-
- app.UseMvc();
}
}
}
diff --git a/samples/OcelotServiceFabric/src/global.json b/samples/OcelotServiceFabric/src/global.json
deleted file mode 100644
index 8288598ab..000000000
--- a/samples/OcelotServiceFabric/src/global.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "projects": [ "../"],
- "sdk": {
- "version": "2.1.4"
- }
-}
\ No newline at end of file
diff --git a/src/Ocelot.Administration/IIdentityServerConfiguration.cs b/src/Ocelot.Administration/IIdentityServerConfiguration.cs
index cbfa289e1..e2260e59a 100644
--- a/src/Ocelot.Administration/IIdentityServerConfiguration.cs
+++ b/src/Ocelot.Administration/IIdentityServerConfiguration.cs
@@ -1,7 +1,7 @@
+using System.Collections.Generic;
+
namespace Ocelot.Administration
{
- using System.Collections.Generic;
-
public interface IIdentityServerConfiguration
{
string ApiName { get; }
diff --git a/src/Ocelot.Administration/IdentityServerConfiguration.cs b/src/Ocelot.Administration/IdentityServerConfiguration.cs
index a560f032d..0e44044fd 100644
--- a/src/Ocelot.Administration/IdentityServerConfiguration.cs
+++ b/src/Ocelot.Administration/IdentityServerConfiguration.cs
@@ -1,7 +1,7 @@
+using System.Collections.Generic;
+
namespace Ocelot.Administration
{
- using System.Collections.Generic;
-
public class IdentityServerConfiguration : IIdentityServerConfiguration
{
public IdentityServerConfiguration(
diff --git a/src/Ocelot.Administration/IdentityServerMiddlewareConfigurationProvider.cs b/src/Ocelot.Administration/IdentityServerMiddlewareConfigurationProvider.cs
index b4b8994a9..1b57a0064 100644
--- a/src/Ocelot.Administration/IdentityServerMiddlewareConfigurationProvider.cs
+++ b/src/Ocelot.Administration/IdentityServerMiddlewareConfigurationProvider.cs
@@ -1,11 +1,14 @@
-namespace Ocelot.Administration
-{
- using Configuration.Repository;
- using Microsoft.AspNetCore.Builder;
- using Microsoft.Extensions.DependencyInjection;
- using Ocelot.Middleware;
- using System.Threading.Tasks;
+using System.Threading.Tasks;
+
+using Ocelot.Configuration.Repository;
+
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.DependencyInjection;
+using Ocelot.Middleware;
+
+namespace Ocelot.Administration
+{
public static class IdentityServerMiddlewareConfigurationProvider
{
public static OcelotMiddlewareConfigurationDelegate Get = builder =>
@@ -27,7 +30,13 @@ public static class IdentityServerMiddlewareConfigurationProvider
}
app.UseAuthentication();
- app.UseMvc();
+ app.UseRouting();
+ app.UseAuthorization();
+ app.UseEndpoints(endpoints =>
+ {
+ endpoints.MapDefaultControllerRoute();
+ endpoints.MapControllers();
+ });
});
}
diff --git a/src/Ocelot.Administration/Ocelot.Administration.csproj b/src/Ocelot.Administration/Ocelot.Administration.csproj
index c1be05990..05419353e 100644
--- a/src/Ocelot.Administration/Ocelot.Administration.csproj
+++ b/src/Ocelot.Administration/Ocelot.Administration.csproj
@@ -1,39 +1,40 @@
-
-
- netcoreapp3.1
- true
- Provides Ocelot extensions to use the administration API and IdentityService dependencies that come with it
- Ocelot.Administration
- 0.0.0-dev
- Ocelot.Administration
- Ocelot.Administration
- API Gateway;.NET core
- https://github.com/ThreeMammals/Ocelot.Administration
- https://github.com/ThreeMammals/Ocelot.Administration
- http://threemammals.com/images/ocelot_logo.png
- win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64
- false
- false
- True
- false
- Tom Pallister
- ..\..\codeanalysis.ruleset
-
-
- full
- True
-
-
-
-
-
-
- all
-
-
-
-
-
-
-
-
+
+
+ net7.0
+ true
+ Provides Ocelot extensions to use the administration API and IdentityService dependencies that come with it
+ Ocelot.Administration
+ 0.0.0-dev
+ Ocelot.Administration
+ Ocelot.Administration
+ API Gateway;.NET core
+ https://github.com/ThreeMammals/Ocelot.Administration
+ https://raw.githubusercontent.com/ThreeMammals/Ocelot/develop/images/ocelot_logo.png
+ win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64
+ false
+ false
+ True
+ false
+ Tom Pallister
+ ..\..\codeanalysis.ruleset
+ True
+ 1591
+
+
+ full
+ True
+
+
+
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/src/Ocelot.Administration/OcelotBuilderExtensions.cs b/src/Ocelot.Administration/OcelotBuilderExtensions.cs
index da25beaa0..23a93460b 100644
--- a/src/Ocelot.Administration/OcelotBuilderExtensions.cs
+++ b/src/Ocelot.Administration/OcelotBuilderExtensions.cs
@@ -1,15 +1,20 @@
-using Ocelot.DependencyInjection;
+using System;
+using System.Collections.Generic;
+using System.IdentityModel.Tokens.Jwt;
+using System.Linq;
+using System.Security.Cryptography.X509Certificates;
+
using IdentityServer4.AccessTokenValidation;
using IdentityServer4.Models;
-using Microsoft.AspNetCore.Builder;
+
+using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.IdentityModel.Tokens;
+
+using Ocelot.DependencyInjection;
using Ocelot.Middleware;
-using System;
-using System.Collections.Generic;
-using System.IdentityModel.Tokens.Jwt;
-using System.Security.Cryptography.X509Certificates;
namespace Ocelot.Administration
{
@@ -18,7 +23,8 @@ public static class OcelotBuilderExtensions
public static IOcelotAdministrationBuilder AddAdministration(this IOcelotBuilder builder, string path, string secret)
{
var administrationPath = new AdministrationPath(path);
- builder.Services.AddSingleton(IdentityServerMiddlewareConfigurationProvider.Get);
+
+ builder.Services.AddSingleton(IdentityServerMiddlewareConfigurationProvider.Get);
//add identity server for admin area
var identityServerConfiguration = IdentityServerConfigurationCreator.GetIdentityServerConfiguration(secret);
@@ -32,10 +38,10 @@ public static IOcelotAdministrationBuilder AddAdministration(this IOcelotBuilder
return new OcelotAdministrationBuilder(builder.Services, builder.Configuration);
}
- public static IOcelotAdministrationBuilder AddAdministration(this IOcelotBuilder builder, string path, Action configureOptions)
+ public static IOcelotAdministrationBuilder AddAdministration(this IOcelotBuilder builder, string path, Action configureOptions)
{
var administrationPath = new AdministrationPath(path);
- builder.Services.AddSingleton(IdentityServerMiddlewareConfigurationProvider.Get);
+ builder.Services.AddSingleton(IdentityServerMiddlewareConfigurationProvider.Get);
if (configureOptions != null)
{
@@ -46,21 +52,23 @@ public static IOcelotAdministrationBuilder AddAdministration(this IOcelotBuilder
return new OcelotAdministrationBuilder(builder.Services, builder.Configuration);
}
- private static void AddIdentityServer(Action configOptions, IOcelotBuilder builder)
+ private static void AddIdentityServer(Action configOptions, IOcelotBuilder builder)
{
builder.Services
.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
- .AddIdentityServerAuthentication(configOptions);
+ .AddJwtBearer("Bearer", configOptions);
}
private static void AddIdentityServer(IIdentityServerConfiguration identityServerConfiguration, IAdministrationPath adminPath, IOcelotBuilder builder, IConfiguration configuration)
{
- builder.Services.TryAddSingleton(identityServerConfiguration);
+ builder.Services.TryAddSingleton(identityServerConfiguration);
var identityServerBuilder = builder.Services
.AddIdentityServer(o =>
{
o.IssuerUri = "Ocelot";
+ o.EmitStaticAudienceClaim = true;
})
+ .AddInMemoryApiScopes(ApiScopes(identityServerConfiguration))
.AddInMemoryApiResources(Resources(identityServerConfiguration))
.AddInMemoryClients(Client(identityServerConfiguration));
@@ -68,14 +76,17 @@ private static void AddIdentityServer(IIdentityServerConfiguration identityServe
var baseSchemeUrlAndPort = urlFinder.Find();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
- builder.Services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
- .AddIdentityServerAuthentication(o =>
+ builder.Services
+ .AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
+ .AddJwtBearer("Bearer", options =>
{
- o.Authority = baseSchemeUrlAndPort + adminPath.Path;
- o.ApiName = identityServerConfiguration.ApiName;
- o.RequireHttpsMetadata = identityServerConfiguration.RequireHttps;
- o.SupportedTokens = SupportedTokens.Both;
- o.ApiSecret = identityServerConfiguration.ApiSecret;
+ options.Authority = baseSchemeUrlAndPort + adminPath.Path;
+ options.RequireHttpsMetadata = identityServerConfiguration.RequireHttps;
+
+ options.TokenValidationParameters = new TokenValidationParameters
+ {
+ ValidateAudience = false,
+ };
});
//todo - refactor naming..
@@ -91,19 +102,24 @@ private static void AddIdentityServer(IIdentityServerConfiguration identityServe
}
}
+ private static IEnumerable ApiScopes(IIdentityServerConfiguration identityServerConfiguration)
+ {
+ return identityServerConfiguration.AllowedScopes.Select(s => new ApiScope(s));
+ }
+
private static List Resources(IIdentityServerConfiguration identityServerConfiguration)
{
return new List
{
- new ApiResource(identityServerConfiguration.ApiName, identityServerConfiguration.ApiName)
+ new(identityServerConfiguration.ApiName, identityServerConfiguration.ApiName)
{
ApiSecrets = new List
{
- new Secret
+ new()
{
- Value = identityServerConfiguration.ApiSecret.Sha256()
- }
- }
+ Value = identityServerConfiguration.ApiSecret.Sha256(),
+ },
+ },
},
};
}
@@ -112,13 +128,13 @@ private static List Client(IIdentityServerConfiguration identityServerCo
{
return new List
{
- new Client
+ new()
{
ClientId = identityServerConfiguration.ApiName,
AllowedGrantTypes = GrantTypes.ClientCredentials,
- ClientSecrets = new List {new Secret(identityServerConfiguration.ApiSecret.Sha256())},
- AllowedScopes = { identityServerConfiguration.ApiName }
- }
+ ClientSecrets = new List {new(identityServerConfiguration.ApiSecret.Sha256())},
+ AllowedScopes = identityServerConfiguration.AllowedScopes,
+ },
};
}
}
diff --git a/src/Ocelot.Cache.CacheManager/Ocelot.Cache.CacheManager.csproj b/src/Ocelot.Cache.CacheManager/Ocelot.Cache.CacheManager.csproj
index 4c575fe54..898dd02c2 100644
--- a/src/Ocelot.Cache.CacheManager/Ocelot.Cache.CacheManager.csproj
+++ b/src/Ocelot.Cache.CacheManager/Ocelot.Cache.CacheManager.csproj
@@ -1,39 +1,41 @@
-
-
- netcoreapp3.1
- true
- Provides Ocelot extensions to use CacheManager.Net
- Ocelot.Cache.CacheManager
- 0.0.0-dev
- Ocelot.Cache.CacheManager
- Ocelot.Cache.CacheManager
- API Gateway;.NET core
- https://github.com/ThreeMammals/Ocelot.Cache.CacheManager
- https://github.com/ThreeMammals/Ocelot.Cache.CacheManager
- win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64
- false
- false
- True
- false
- Tom Pallister
- ..\..\codeanalysis.ruleset
-
-
- full
- True
-
-
-
-
-
-
- all
-
-
-
-
-
-
-
-
-
+
+
+ net7.0
+ true
+ Provides Ocelot extensions to use CacheManager.Net
+ Ocelot.Cache.CacheManager
+ 0.0.0-dev
+ Ocelot.Cache.CacheManager
+ Ocelot.Cache.CacheManager
+ API Gateway;.NET core
+ https://github.com/ThreeMammals/Ocelot.Cache.CacheManager
+ https://raw.githubusercontent.com/ThreeMammals/Ocelot/develop/images/ocelot_logo.png
+ win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64
+ false
+ false
+ True
+ false
+ Tom Pallister
+ ..\..\codeanalysis.ruleset
+ True
+ 1591
+
+
+ full
+ True
+
+
+
+
+
+
+ all
+
+
+
+
+
+
+
+
+
diff --git a/src/Ocelot.Cache.CacheManager/OcelotBuilderExtensions.cs b/src/Ocelot.Cache.CacheManager/OcelotBuilderExtensions.cs
index 7a2c2d868..f9799607c 100644
--- a/src/Ocelot.Cache.CacheManager/OcelotBuilderExtensions.cs
+++ b/src/Ocelot.Cache.CacheManager/OcelotBuilderExtensions.cs
@@ -1,13 +1,17 @@
-namespace Ocelot.Cache.CacheManager
-{
- using Configuration;
- using Configuration.File;
- using DependencyInjection;
- using global::CacheManager.Core;
- using Microsoft.Extensions.DependencyInjection;
- using Microsoft.Extensions.DependencyInjection.Extensions;
- using System;
+using System;
+
+using Ocelot.Configuration;
+using Ocelot.Configuration.File;
+
+using Ocelot.DependencyInjection;
+using global::CacheManager.Core;
+
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+
+namespace Ocelot.Cache.CacheManager
+{
public static class OcelotBuilderExtensions
{
public static IOcelotBuilder AddCacheManager(this IOcelotBuilder builder, Action settings)
@@ -17,21 +21,21 @@ public static IOcelotBuilder AddCacheManager(this IOcelotBuilder builder, Action
builder.Services.RemoveAll(typeof(ICacheManager));
builder.Services.RemoveAll(typeof(IOcelotCache));
- builder.Services.AddSingleton>(cacheManagerOutputCache);
+ builder.Services.AddSingleton(cacheManagerOutputCache);
builder.Services.AddSingleton>(ocelotOutputCacheManager);
var ocelotConfigCacheManagerOutputCache = CacheFactory.Build("OcelotConfigurationCache", settings);
var ocelotConfigCacheManager = new OcelotCacheManagerCache(ocelotConfigCacheManagerOutputCache);
builder.Services.RemoveAll(typeof(ICacheManager));
builder.Services.RemoveAll(typeof(IOcelotCache));
- builder.Services.AddSingleton>(ocelotConfigCacheManagerOutputCache);
+ builder.Services.AddSingleton(ocelotConfigCacheManagerOutputCache);
builder.Services.AddSingleton>(ocelotConfigCacheManager);
var fileConfigCacheManagerOutputCache = CacheFactory.Build("FileConfigurationCache", settings);
var fileConfigCacheManager = new OcelotCacheManagerCache(fileConfigCacheManagerOutputCache);
builder.Services.RemoveAll(typeof(ICacheManager));
builder.Services.RemoveAll(typeof(IOcelotCache));
- builder.Services.AddSingleton>(fileConfigCacheManagerOutputCache);
+ builder.Services.AddSingleton(fileConfigCacheManagerOutputCache);
builder.Services.AddSingleton>(fileConfigCacheManager);
builder.Services.RemoveAll(typeof(ICacheKeyGenerator));
diff --git a/src/Ocelot.Cache.CacheManager/OcelotCacheManagerCache.cs b/src/Ocelot.Cache.CacheManager/OcelotCacheManagerCache.cs
index a95df4c4f..b4b31aa65 100644
--- a/src/Ocelot.Cache.CacheManager/OcelotCacheManagerCache.cs
+++ b/src/Ocelot.Cache.CacheManager/OcelotCacheManagerCache.cs
@@ -1,8 +1,9 @@
-namespace Ocelot.Cache.CacheManager
-{
- using global::CacheManager.Core;
- using System;
+using System;
+
+using global::CacheManager.Core;
+namespace Ocelot.Cache.CacheManager
+{
public class OcelotCacheManagerCache : IOcelotCache
{
private readonly ICacheManager _cacheManager;
diff --git a/src/Ocelot.Provider.Consul/Consul.cs b/src/Ocelot.Provider.Consul/Consul.cs
index 72f15d095..2e73f5271 100644
--- a/src/Ocelot.Provider.Consul/Consul.cs
+++ b/src/Ocelot.Provider.Consul/Consul.cs
@@ -1,15 +1,20 @@
-namespace Ocelot.Provider.Consul
-{
- using global::Consul;
- using Infrastructure.Extensions;
- using Logging;
- using ServiceDiscovery.Providers;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading.Tasks;
- using Values;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+using global::Consul;
+
+using Ocelot.Infrastructure.Extensions;
+
+using Ocelot.Logging;
+using Ocelot.ServiceDiscovery.Providers;
+
+using Ocelot.Values;
+
+namespace Ocelot.Provider.Consul
+{
public class Consul : IServiceDiscoveryProvider
{
private readonly ConsulRegistryConfiguration _config;
@@ -54,7 +59,7 @@ public async Task> Get()
return services.ToList();
}
- private Service BuildService(ServiceEntry serviceEntry, Node serviceNode)
+ private static Service BuildService(ServiceEntry serviceEntry, Node serviceNode)
{
return new Service(
serviceEntry.Service.Service,
@@ -64,7 +69,7 @@ private Service BuildService(ServiceEntry serviceEntry, Node serviceNode)
serviceEntry.Service.Tags ?? Enumerable.Empty());
}
- private bool IsValid(ServiceEntry serviceEntry)
+ private static bool IsValid(ServiceEntry serviceEntry)
{
if (string.IsNullOrEmpty(serviceEntry.Service.Address) || serviceEntry.Service.Address.Contains("http://") || serviceEntry.Service.Address.Contains("https://") || serviceEntry.Service.Port <= 0)
{
@@ -74,7 +79,7 @@ private bool IsValid(ServiceEntry serviceEntry)
return true;
}
- private string GetVersionFromStrings(IEnumerable strings)
+ private static string GetVersionFromStrings(IEnumerable strings)
{
return strings
?.FirstOrDefault(x => x.StartsWith(VersionPrefix, StringComparison.Ordinal))
diff --git a/src/Ocelot.Provider.Consul/ConsulClientFactory.cs b/src/Ocelot.Provider.Consul/ConsulClientFactory.cs
index f3f0e54f6..c8142026f 100644
--- a/src/Ocelot.Provider.Consul/ConsulClientFactory.cs
+++ b/src/Ocelot.Provider.Consul/ConsulClientFactory.cs
@@ -1,15 +1,16 @@
-namespace Ocelot.Provider.Consul
-{
- using global::Consul;
- using System;
+using System;
+
+using global::Consul;
+namespace Ocelot.Provider.Consul
+{
public class ConsulClientFactory : IConsulClientFactory
{
public IConsulClient Get(ConsulRegistryConfiguration config)
{
return new ConsulClient(c =>
{
- c.Address = new Uri($"http://{config.Host}:{config.Port}");
+ c.Address = new Uri($"{config.Scheme}://{config.Host}:{config.Port}");
if (!string.IsNullOrEmpty(config?.Token))
{
diff --git a/src/Ocelot.Provider.Consul/ConsulFileConfigurationRepository.cs b/src/Ocelot.Provider.Consul/ConsulFileConfigurationRepository.cs
index 1044b7b62..e67fd83eb 100644
--- a/src/Ocelot.Provider.Consul/ConsulFileConfigurationRepository.cs
+++ b/src/Ocelot.Provider.Consul/ConsulFileConfigurationRepository.cs
@@ -1,16 +1,22 @@
-namespace Ocelot.Provider.Consul
-{
- using Configuration.File;
- using Configuration.Repository;
- using global::Consul;
- using Logging;
- using Microsoft.Extensions.Options;
- using Newtonsoft.Json;
- using Responses;
- using System;
- using System.Text;
- using System.Threading.Tasks;
+using System;
+using System.Text;
+using System.Threading.Tasks;
+
+using Ocelot.Configuration.File;
+using Ocelot.Configuration.Repository;
+
+using global::Consul;
+
+using Ocelot.Logging;
+using Microsoft.Extensions.Options;
+
+using Newtonsoft.Json;
+
+using Ocelot.Responses;
+
+namespace Ocelot.Provider.Consul
+{
public class ConsulFileConfigurationRepository : IFileConfigurationRepository
{
private readonly IConsulClient _consul;
@@ -31,7 +37,7 @@ public ConsulFileConfigurationRepository(
_configurationKey = string.IsNullOrWhiteSpace(serviceDiscoveryProvider.ConfigurationKey) ? "InternalConfiguration" :
serviceDiscoveryProvider.ConfigurationKey;
- var config = new ConsulRegistryConfiguration(serviceDiscoveryProvider.Host,
+ var config = new ConsulRegistryConfiguration(serviceDiscoveryProvider.Scheme, serviceDiscoveryProvider.Host,
serviceDiscoveryProvider.Port, _configurationKey, serviceDiscoveryProvider.Token);
_consul = factory.Get(config);
@@ -70,7 +76,7 @@ public async Task Set(FileConfiguration ocelotConfiguration)
var kvPair = new KVPair(_configurationKey)
{
- Value = bytes
+ Value = bytes,
};
var result = await _consul.KV.Put(kvPair);
diff --git a/src/Ocelot.Provider.Consul/ConsulMiddlewareConfigurationProvider.cs b/src/Ocelot.Provider.Consul/ConsulMiddlewareConfigurationProvider.cs
index 1b89b4f33..4be20d55d 100644
--- a/src/Ocelot.Provider.Consul/ConsulMiddlewareConfigurationProvider.cs
+++ b/src/Ocelot.Provider.Consul/ConsulMiddlewareConfigurationProvider.cs
@@ -1,17 +1,21 @@
-namespace Ocelot.Provider.Consul
-{
- using Configuration.Creator;
- using Configuration.File;
- using Configuration.Repository;
- using Microsoft.AspNetCore.Builder;
- using Microsoft.Extensions.DependencyInjection;
- using Microsoft.Extensions.Options;
- using Middleware;
- using Responses;
- using System;
- using System.Linq;
- using System.Threading.Tasks;
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+
+using Ocelot.Configuration.Creator;
+using Ocelot.Configuration.File;
+using Ocelot.Configuration.Repository;
+
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using Ocelot.Middleware;
+
+using Ocelot.Responses;
+
+namespace Ocelot.Provider.Consul
+{
public static class ConsulMiddlewareConfigurationProvider
{
public static OcelotMiddlewareConfigurationDelegate Get = async builder =>
@@ -77,7 +81,7 @@ private static async Task SetFileConfigInConsul(IApplicationBuilder builder,
private static void ThrowToStopOcelotStarting(Response config)
{
- throw new Exception($"Unable to start Ocelot, errors are: {string.Join(",", config.Errors.Select(x => x.ToString()))}");
+ throw new Exception($"Unable to start Ocelot, errors are: {string.Join(',', config.Errors.Select(x => x.ToString()))}");
}
private static bool IsError(Response response)
diff --git a/src/Ocelot.Provider.Consul/ConsulProviderFactory.cs b/src/Ocelot.Provider.Consul/ConsulProviderFactory.cs
index 8cffa8825..9392f461b 100644
--- a/src/Ocelot.Provider.Consul/ConsulProviderFactory.cs
+++ b/src/Ocelot.Provider.Consul/ConsulProviderFactory.cs
@@ -1,27 +1,29 @@
-namespace Ocelot.Provider.Consul
-{
- using Logging;
- using Microsoft.Extensions.DependencyInjection;
- using ServiceDiscovery;
+using Ocelot.Logging;
- public static class ConsulProviderFactory
- {
- public static ServiceDiscoveryFinderDelegate Get = (provider, config, reRoute) =>
- {
- var factory = provider.GetService();
+using Microsoft.Extensions.DependencyInjection;
- var consulFactory = provider.GetService();
-
- var consulRegistryConfiguration = new ConsulRegistryConfiguration(config.Host, config.Port, reRoute.ServiceName, config.Token);
-
- var consulServiceDiscoveryProvider = new Consul(consulRegistryConfiguration, factory, consulFactory);
-
- if (config.Type?.ToLower() == "pollconsul")
- {
- return new PollConsul(config.PollingInterval, factory, consulServiceDiscoveryProvider);
- }
-
- return consulServiceDiscoveryProvider;
- };
- }
-}
+using Ocelot.ServiceDiscovery;
+
+namespace Ocelot.Provider.Consul
+{
+ public static class ConsulProviderFactory
+ {
+ public static ServiceDiscoveryFinderDelegate Get = (provider, config, route) =>
+ {
+ var factory = provider.GetService();
+
+ var consulFactory = provider.GetService();
+
+ var consulRegistryConfiguration = new ConsulRegistryConfiguration(config.Scheme, config.Host, config.Port, route.ServiceName, config.Token);
+
+ var consulServiceDiscoveryProvider = new Consul(consulRegistryConfiguration, factory, consulFactory);
+
+ if (config.Type?.ToLower() == "pollconsul")
+ {
+ return new PollConsul(config.PollingInterval, factory, consulServiceDiscoveryProvider);
+ }
+
+ return consulServiceDiscoveryProvider;
+ };
+ }
+}
diff --git a/src/Ocelot.Provider.Consul/ConsulRegistryConfiguration.cs b/src/Ocelot.Provider.Consul/ConsulRegistryConfiguration.cs
index a84088b01..38bf515fa 100644
--- a/src/Ocelot.Provider.Consul/ConsulRegistryConfiguration.cs
+++ b/src/Ocelot.Provider.Consul/ConsulRegistryConfiguration.cs
@@ -2,15 +2,17 @@
{
public class ConsulRegistryConfiguration
{
- public ConsulRegistryConfiguration(string host, int port, string keyOfServiceInConsul, string token)
+ public ConsulRegistryConfiguration(string scheme, string host, int port, string keyOfServiceInConsul, string token)
{
Host = string.IsNullOrEmpty(host) ? "localhost" : host;
Port = port > 0 ? port : 8500;
+ Scheme = string.IsNullOrEmpty(scheme) ? "http" : scheme;
KeyOfServiceInConsul = keyOfServiceInConsul;
Token = token;
}
public string KeyOfServiceInConsul { get; }
+ public string Scheme { get; }
public string Host { get; }
public int Port { get; }
public string Token { get; }
diff --git a/src/Ocelot.Provider.Consul/IConsulClientFactory.cs b/src/Ocelot.Provider.Consul/IConsulClientFactory.cs
index 3710a8181..f9430dec7 100644
--- a/src/Ocelot.Provider.Consul/IConsulClientFactory.cs
+++ b/src/Ocelot.Provider.Consul/IConsulClientFactory.cs
@@ -1,7 +1,7 @@
-namespace Ocelot.Provider.Consul
-{
- using global::Consul;
+using global::Consul;
+namespace Ocelot.Provider.Consul
+{
public interface IConsulClientFactory
{
IConsulClient Get(ConsulRegistryConfiguration config);
diff --git a/src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj b/src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj
index ece85c9b3..fc725e207 100644
--- a/src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj
+++ b/src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj
@@ -1,38 +1,39 @@
-
-
- netcoreapp3.1
- true
- Provides Ocelot extensions to use Consul
- Ocelot.Provider.Consul
- 0.0.0-dev
- Ocelot.Provider.Consul
- Ocelot.Provider.Consul
- API Gateway;.NET core
- https://github.com/ThreeMammals/Ocelot.Provider.Consul
- https://github.com/ThreeMammals/Ocelot.Provider.Consul
- http://threemammals.com/images/ocelot_logo.png
- win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64
- false
- false
- True
- false
- Tom Pallister
- ..\..\codeanalysis.ruleset
-
-
- full
- True
-
-
-
-
-
-
-
- all
-
-
-
-
-
-
+
+
+ net7.0
+ true
+ Provides Ocelot extensions to use Consul
+ Ocelot.Provider.Consul
+ 0.0.0-dev
+ Ocelot.Provider.Consul
+ Ocelot.Provider.Consul
+ API Gateway;.NET core
+ https://github.com/ThreeMammals/Ocelot.Provider.Consul
+ https://raw.githubusercontent.com/ThreeMammals/Ocelot/develop/images/ocelot_logo.png
+ win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64
+ false
+ false
+ True
+ false
+ Tom Pallister
+ ..\..\codeanalysis.ruleset
+ True
+ 1591
+
+
+ full
+ True
+
+
+
+
+
+
+
+ all
+
+
+
+
+
+
diff --git a/src/Ocelot.Provider.Consul/OcelotBuilderExtensions.cs b/src/Ocelot.Provider.Consul/OcelotBuilderExtensions.cs
index ce2ed95bb..40548178c 100644
--- a/src/Ocelot.Provider.Consul/OcelotBuilderExtensions.cs
+++ b/src/Ocelot.Provider.Consul/OcelotBuilderExtensions.cs
@@ -1,17 +1,17 @@
-namespace Ocelot.Provider.Consul
-{
- using Configuration.Repository;
- using DependencyInjection;
- using Microsoft.Extensions.DependencyInjection;
- using Microsoft.Extensions.DependencyInjection.Extensions;
- using Middleware;
- using ServiceDiscovery;
+using Ocelot.Configuration.Repository;
+
+using Ocelot.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+
+namespace Ocelot.Provider.Consul
+{
public static class OcelotBuilderExtensions
{
public static IOcelotBuilder AddConsul(this IOcelotBuilder builder)
{
- builder.Services.AddSingleton(ConsulProviderFactory.Get);
+ builder.Services.AddSingleton(ConsulProviderFactory.Get);
builder.Services.AddSingleton();
builder.Services.RemoveAll(typeof(IFileConfigurationPollerOptions));
builder.Services.AddSingleton();
@@ -20,7 +20,7 @@ public static IOcelotBuilder AddConsul(this IOcelotBuilder builder)
public static IOcelotBuilder AddConfigStoredInConsul(this IOcelotBuilder builder)
{
- builder.Services.AddSingleton(ConsulMiddlewareConfigurationProvider.Get);
+ builder.Services.AddSingleton(ConsulMiddlewareConfigurationProvider.Get);
builder.Services.AddHostedService();
builder.Services.AddSingleton();
return builder;
diff --git a/src/Ocelot.Provider.Consul/PollConsul.cs b/src/Ocelot.Provider.Consul/PollConsul.cs
new file mode 100644
index 000000000..b5ac65d86
--- /dev/null
+++ b/src/Ocelot.Provider.Consul/PollConsul.cs
@@ -0,0 +1,53 @@
+using Ocelot.Logging;
+using Ocelot.ServiceDiscovery.Providers;
+using Ocelot.Values;
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Ocelot.Provider.Consul;
+
+public sealed class PollConsul : IServiceDiscoveryProvider, IDisposable
+{
+ private readonly IOcelotLogger _logger;
+ private readonly IServiceDiscoveryProvider _consulServiceDiscoveryProvider;
+ private Timer _timer;
+ private bool _polling;
+ private List _services;
+
+ public PollConsul(int pollingInterval, IOcelotLoggerFactory factory, IServiceDiscoveryProvider consulServiceDiscoveryProvider)
+ {
+ _logger = factory.CreateLogger();
+ _consulServiceDiscoveryProvider = consulServiceDiscoveryProvider;
+ _services = new List();
+
+ _timer = new Timer(async x =>
+ {
+ if (_polling)
+ {
+ return;
+ }
+
+ _polling = true;
+ await Poll();
+ _polling = false;
+ }, null, pollingInterval, pollingInterval);
+ }
+
+ public void Dispose()
+ {
+ _timer?.Dispose();
+ _timer = null;
+ }
+
+ public Task> Get()
+ {
+ return Task.FromResult(_services);
+ }
+
+ private async Task Poll()
+ {
+ _services = await _consulServiceDiscoveryProvider.Get();
+ }
+}
diff --git a/src/Ocelot.Provider.Consul/PollingConsulServiceDiscoveryProvider.cs b/src/Ocelot.Provider.Consul/PollingConsulServiceDiscoveryProvider.cs
deleted file mode 100644
index 5bace8021..000000000
--- a/src/Ocelot.Provider.Consul/PollingConsulServiceDiscoveryProvider.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-namespace Ocelot.Provider.Consul
-{
- using Logging;
- using ServiceDiscovery.Providers;
- using System;
- using System.Collections.Generic;
- using System.Threading;
- using System.Threading.Tasks;
- using Values;
-
- public sealed class PollConsul : IServiceDiscoveryProvider, IDisposable
- {
- private readonly IOcelotLogger _logger;
- private readonly IServiceDiscoveryProvider _consulServiceDiscoveryProvider;
- private Timer _timer;
- private bool _polling;
- private List _services;
-
- public PollConsul(int pollingInterval, IOcelotLoggerFactory factory, IServiceDiscoveryProvider consulServiceDiscoveryProvider)
- {
- _logger = factory.CreateLogger();
- _consulServiceDiscoveryProvider = consulServiceDiscoveryProvider;
- _services = new List();
-
- _timer = new Timer(async x =>
- {
- if (_polling)
- {
- return;
- }
-
- _polling = true;
- await Poll();
- _polling = false;
- }, null, pollingInterval, pollingInterval);
- }
-
- public void Dispose()
- {
- _timer?.Dispose();
- _timer = null;
- }
-
- public Task> Get()
- {
- return Task.FromResult(_services);
- }
-
- private async Task Poll()
- {
- _services = await _consulServiceDiscoveryProvider.Get();
- }
- }
-}
diff --git a/src/Ocelot.Provider.Consul/UnableToSetConfigInConsulError.cs b/src/Ocelot.Provider.Consul/UnableToSetConfigInConsulError.cs
index bc3103ace..abf763735 100644
--- a/src/Ocelot.Provider.Consul/UnableToSetConfigInConsulError.cs
+++ b/src/Ocelot.Provider.Consul/UnableToSetConfigInConsulError.cs
@@ -1,11 +1,11 @@
-namespace Ocelot.Provider.Consul
-{
- using Errors;
+using Ocelot.Errors;
+namespace Ocelot.Provider.Consul
+{
public class UnableToSetConfigInConsulError : Error
{
public UnableToSetConfigInConsulError(string s)
- : base(s, OcelotErrorCode.UnknownError)
+ : base(s, OcelotErrorCode.UnknownError, 404)
{
}
}
diff --git a/src/Ocelot.Provider.Eureka/Eureka.cs b/src/Ocelot.Provider.Eureka/Eureka.cs
index 8a064549e..b98fc9d04 100644
--- a/src/Ocelot.Provider.Eureka/Eureka.cs
+++ b/src/Ocelot.Provider.Eureka/Eureka.cs
@@ -1,35 +1,38 @@
-namespace Ocelot.Provider.Eureka
-{
- using ServiceDiscovery.Providers;
- using Steeltoe.Common.Discovery;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading.Tasks;
- using Values;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
- public class Eureka : IServiceDiscoveryProvider
- {
- private readonly IDiscoveryClient _client;
- private readonly string _serviceName;
+using Ocelot.ServiceDiscovery.Providers;
- public Eureka(string serviceName, IDiscoveryClient client)
- {
- _client = client;
- _serviceName = serviceName;
- }
+using Steeltoe.Discovery;
- public Task> Get()
- {
- var services = new List();
-
- var instances = _client.GetInstances(_serviceName);
-
- if (instances != null && instances.Any())
- {
- services.AddRange(instances.Select(i => new Service(i.ServiceId, new ServiceHostAndPort(i.Host, i.Port, i.Uri.Scheme), "", "", new List())));
- }
-
- return Task.FromResult(services);
- }
- }
-}
+using Ocelot.Values;
+
+namespace Ocelot.Provider.Eureka
+{
+ public class Eureka : IServiceDiscoveryProvider
+ {
+ private readonly IDiscoveryClient _client;
+ private readonly string _serviceName;
+
+ public Eureka(string serviceName, IDiscoveryClient client)
+ {
+ _client = client;
+ _serviceName = serviceName;
+ }
+
+ public Task> Get()
+ {
+ var services = new List();
+
+ var instances = _client.GetInstances(_serviceName);
+
+ if (instances != null && instances.Any())
+ {
+ services.AddRange(instances.Select(i => new Service(i.ServiceId, new ServiceHostAndPort(i.Host, i.Port, i.Uri.Scheme), string.Empty, string.Empty, new List())));
+ }
+
+ return Task.FromResult(services);
+ }
+ }
+}
diff --git a/src/Ocelot.Provider.Eureka/EurekaMiddlewareConfigurationProvider.cs b/src/Ocelot.Provider.Eureka/EurekaMiddlewareConfigurationProvider.cs
index f893ed722..5aec5708b 100644
--- a/src/Ocelot.Provider.Eureka/EurekaMiddlewareConfigurationProvider.cs
+++ b/src/Ocelot.Provider.Eureka/EurekaMiddlewareConfigurationProvider.cs
@@ -1,15 +1,19 @@
-namespace Ocelot.Provider.Eureka
-{
- using Configuration;
- using Configuration.Repository;
- using Microsoft.Extensions.DependencyInjection;
- using Middleware;
- using Steeltoe.Discovery.Client;
- using System.Threading.Tasks;
+using System.Threading.Tasks;
+
+using Ocelot.Configuration;
+using Ocelot.Configuration.Repository;
+
+using Microsoft.Extensions.DependencyInjection;
+using Ocelot.Middleware;
+
+using Steeltoe.Discovery.Client;
+
+namespace Ocelot.Provider.Eureka
+{
public class EurekaMiddlewareConfigurationProvider
{
- public static OcelotMiddlewareConfigurationDelegate Get = builder =>
+ public static OcelotMiddlewareConfigurationDelegate Get { get; } = builder =>
{
var internalConfigRepo = builder.ApplicationServices.GetService();
@@ -17,7 +21,7 @@ public class EurekaMiddlewareConfigurationProvider
if (UsingEurekaServiceDiscoveryProvider(config.Data))
{
- builder.UseDiscoveryClient();
+ //builder.UseDiscoveryClient();
}
return Task.CompletedTask;
diff --git a/src/Ocelot.Provider.Eureka/EurekaProviderFactory.cs b/src/Ocelot.Provider.Eureka/EurekaProviderFactory.cs
index d9bba1791..a1d4f522a 100644
--- a/src/Ocelot.Provider.Eureka/EurekaProviderFactory.cs
+++ b/src/Ocelot.Provider.Eureka/EurekaProviderFactory.cs
@@ -1,18 +1,20 @@
-namespace Ocelot.Provider.Eureka
-{
- using Microsoft.Extensions.DependencyInjection;
- using ServiceDiscovery;
- using Steeltoe.Common.Discovery;
+using Microsoft.Extensions.DependencyInjection;
+
+using Ocelot.ServiceDiscovery;
+using Steeltoe.Discovery;
+
+namespace Ocelot.Provider.Eureka
+{
public static class EurekaProviderFactory
{
- public static ServiceDiscoveryFinderDelegate Get = (provider, config, reRoute) =>
+ public static ServiceDiscoveryFinderDelegate Get = (provider, config, route) =>
{
var client = provider.GetService();
if (config.Type?.ToLower() == "eureka" && client != null)
{
- return new Eureka(reRoute.ServiceName, client);
+ return new Eureka(route.ServiceName, client);
}
return null;
diff --git a/src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj b/src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj
index 587071e03..f9543b810 100644
--- a/src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj
+++ b/src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj
@@ -1,6 +1,6 @@
- netcoreapp3.1
+ net7.0
true
Provides Ocelot extensions to use Eureka
Ocelot.Provider.Eureka
@@ -10,7 +10,7 @@
API Gateway;.NET core
https://github.com/ThreeMammals/Ocelot.Provider.Eureka
https://github.com/ThreeMammals/Ocelot.Provider.Eureka
- http://threemammals.com/images/ocelot_logo.png
+ https://raw.githubusercontent.com/ThreeMammals/Ocelot/develop/images/ocelot_logo.png
win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64
false
false
@@ -18,6 +18,8 @@
false
Tom Pallister
..\..\codeanalysis.ruleset
+ True
+ 1591
full
@@ -27,8 +29,9 @@
-
-
+
+
+
all
diff --git a/src/Ocelot.Provider.Eureka/OcelotBuilderExtensions.cs b/src/Ocelot.Provider.Eureka/OcelotBuilderExtensions.cs
index e141d1947..0f3d42f61 100644
--- a/src/Ocelot.Provider.Eureka/OcelotBuilderExtensions.cs
+++ b/src/Ocelot.Provider.Eureka/OcelotBuilderExtensions.cs
@@ -1,20 +1,18 @@
-namespace Ocelot.Provider.Eureka
-{
- using DependencyInjection;
- using Microsoft.Extensions.Configuration;
- using Microsoft.Extensions.DependencyInjection;
- using Middleware;
- using ServiceDiscovery;
- using Steeltoe.Discovery.Client;
- using System.Linq;
+using Ocelot.DependencyInjection;
+
+using Microsoft.Extensions.DependencyInjection;
+using Steeltoe.Discovery.Client;
+
+namespace Ocelot.Provider.Eureka
+{
public static class OcelotBuilderExtensions
{
public static IOcelotBuilder AddEureka(this IOcelotBuilder builder)
{
builder.Services.AddDiscoveryClient(builder.Configuration);
- builder.Services.AddSingleton(EurekaProviderFactory.Get);
- builder.Services.AddSingleton(EurekaMiddlewareConfigurationProvider.Get);
+ builder.Services.AddSingleton(EurekaProviderFactory.Get);
+ builder.Services.AddSingleton(EurekaMiddlewareConfigurationProvider.Get);
return builder;
}
}
diff --git a/src/Ocelot.Provider.Kubernetes/KubeApiClientExtensions/EndPointClientV1.cs b/src/Ocelot.Provider.Kubernetes/KubeApiClientExtensions/EndPointClientV1.cs
new file mode 100644
index 000000000..cb6bd2aab
--- /dev/null
+++ b/src/Ocelot.Provider.Kubernetes/KubeApiClientExtensions/EndPointClientV1.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+using HTTPlease;
+
+using KubeClient;
+using KubeClient.Models;
+using KubeClient.ResourceClients;
+
+namespace Ocelot.Provider.Kubernetes.KubeApiClientExtensions
+{
+ public class EndPointClientV1 : KubeResourceClient
+ {
+ private readonly HttpRequest _collection = KubeRequest.Create("api/v1/namespaces/{Namespace}/endpoints/{ServiceName}");
+
+ public EndPointClientV1(IKubeApiClient client) : base(client)
+ {
+ }
+
+ public async Task Get(string serviceName, string kubeNamespace = null, CancellationToken cancellationToken = default)
+ {
+ if (string.IsNullOrEmpty(serviceName))
+ {
+ throw new ArgumentNullException(nameof(serviceName));
+ }
+
+ var response = await Http.GetAsync(
+ _collection.WithTemplateParameters(new
+ {
+ Namespace = kubeNamespace ?? KubeClient.DefaultNamespace,
+ ServiceName = serviceName,
+ }),
+ cancellationToken
+ );
+
+ if (response.IsSuccessStatusCode)
+ {
+ return await response.ReadContentAsAsync();
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/Ocelot.Provider.Kubernetes/KubeProvider.cs b/src/Ocelot.Provider.Kubernetes/KubeProvider.cs
deleted file mode 100644
index 22f5b1153..000000000
--- a/src/Ocelot.Provider.Kubernetes/KubeProvider.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using KubeClient;
-using KubeClient.Models;
-using Ocelot.Logging;
-using Ocelot.ServiceDiscovery.Providers;
-using Ocelot.Values;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-
-namespace Ocelot.Provider.Kubernetes
-{
- public class Kube : IServiceDiscoveryProvider
- {
- private KubeRegistryConfiguration kubeRegistryConfiguration;
- private IOcelotLogger logger;
- private IKubeApiClient kubeApi;
-
- public Kube(KubeRegistryConfiguration kubeRegistryConfiguration, IOcelotLoggerFactory factory, IKubeApiClient kubeApi)
- {
- this.kubeRegistryConfiguration = kubeRegistryConfiguration;
- this.logger = factory.CreateLogger();
- this.kubeApi = kubeApi;
- }
-
-
- public async Task> Get()
- {
- var service = await kubeApi.ServicesV1().Get(kubeRegistryConfiguration.KeyOfServiceInK8s, kubeRegistryConfiguration.KubeNamespace);
- var services = new List();
- if (IsValid(service))
- {
- services.Add(BuildService(service));
- }
- 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");
- }
- return services;
- }
-
- private bool IsValid(ServiceV1 service)
- {
- if (string.IsNullOrEmpty(service.Spec.ClusterIP) || service.Spec.Ports.Count <= 0)
- {
- return false;
- }
-
- return true;
- }
-
- private Service BuildService(ServiceV1 serviceEntry)
- {
- var servicePort = serviceEntry.Spec.Ports.FirstOrDefault();
- return new Service(
- serviceEntry.Metadata.Name,
- new ServiceHostAndPort(serviceEntry.Spec.ClusterIP, servicePort.Port),
- serviceEntry.Metadata.Uid,
- string.Empty,
- Enumerable.Empty());
- }
- }
-}
diff --git a/src/Ocelot.Provider.Kubernetes/KubernetesProviderFactory.cs b/src/Ocelot.Provider.Kubernetes/KubernetesProviderFactory.cs
index f12d0ebc0..0cfc2a23e 100644
--- a/src/Ocelot.Provider.Kubernetes/KubernetesProviderFactory.cs
+++ b/src/Ocelot.Provider.Kubernetes/KubernetesProviderFactory.cs
@@ -1,35 +1,41 @@
-using KubeClient;
-using Microsoft.Extensions.DependencyInjection;
-using Ocelot.Logging;
-using Ocelot.ServiceDiscovery;
-using System;
-using Ocelot.Configuration;
+using System;
-namespace Ocelot.Provider.Kubernetes
-{
- public static class KubernetesProviderFactory
- {
- public static ServiceDiscoveryFinderDelegate Get = (provider, config, reRoute) =>
- {
- var factory = provider.GetService();
- return GetkubeProvider(provider, config, reRoute, factory);
- };
+using KubeClient;
- private static ServiceDiscovery.Providers.IServiceDiscoveryProvider GetkubeProvider(IServiceProvider provider, Configuration.ServiceProviderConfiguration config, DownstreamReRoute reRoute, IOcelotLoggerFactory factory)
- {
- var kubeClient = provider.GetService();
- var k8sRegistryConfiguration = new KubeRegistryConfiguration()
- {
- KeyOfServiceInK8s = reRoute.ServiceName,
- KubeNamespace = string.IsNullOrEmpty(reRoute.ServiceNamespace) ? config.Namespace : reRoute.ServiceNamespace
- };
+using Microsoft.Extensions.DependencyInjection;
- var k8sServiceDiscoveryProvider = new Kube(k8sRegistryConfiguration, factory, kubeClient);
- if (config.Type?.ToLower() == "pollkube")
- {
- return new PollKube(config.PollingInterval, factory, k8sServiceDiscoveryProvider);
- }
- return k8sServiceDiscoveryProvider;
- }
- }
-}
+using Ocelot.Configuration;
+using Ocelot.Logging;
+using Ocelot.ServiceDiscovery;
+
+namespace Ocelot.Provider.Kubernetes
+{
+ public static class KubernetesProviderFactory
+ {
+ public static ServiceDiscoveryFinderDelegate Get = (provider, config, route) =>
+ {
+ var factory = provider.GetService();
+ return GetKubeProvider(provider, config, route, factory);
+ };
+
+ private static ServiceDiscovery.Providers.IServiceDiscoveryProvider GetKubeProvider(IServiceProvider provider, ServiceProviderConfiguration config, DownstreamRoute route, IOcelotLoggerFactory factory)
+ {
+ var kubeClient = provider.GetService();
+
+ var k8sRegistryConfiguration = new KubeRegistryConfiguration
+ {
+ KeyOfServiceInK8s = route.ServiceName,
+ KubeNamespace = string.IsNullOrEmpty(route.ServiceNamespace) ? config.Namespace : route.ServiceNamespace,
+ };
+
+ var k8sServiceDiscoveryProvider = new KubernetesServiceDiscoveryProvider(k8sRegistryConfiguration, factory, kubeClient);
+
+ if (config.Type?.ToLower() == "pollkube")
+ {
+ return new PollKubernetes(config.PollingInterval, factory, k8sServiceDiscoveryProvider);
+ }
+
+ return k8sServiceDiscoveryProvider;
+ }
+ }
+}
diff --git a/src/Ocelot.Provider.Kubernetes/KubernetesServiceDiscoveryProvider.cs b/src/Ocelot.Provider.Kubernetes/KubernetesServiceDiscoveryProvider.cs
new file mode 100644
index 000000000..dabf6dae4
--- /dev/null
+++ b/src/Ocelot.Provider.Kubernetes/KubernetesServiceDiscoveryProvider.cs
@@ -0,0 +1,58 @@
+using KubeClient;
+using KubeClient.Models;
+using Ocelot.Logging;
+using Ocelot.Provider.Kubernetes.KubeApiClientExtensions;
+using Ocelot.ServiceDiscovery.Providers;
+using Ocelot.Values;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Ocelot.Provider.Kubernetes;
+
+public class KubernetesServiceDiscoveryProvider : IServiceDiscoveryProvider
+{
+ private readonly KubeRegistryConfiguration _kubeRegistryConfiguration;
+ private readonly IOcelotLogger _logger;
+ private readonly IKubeApiClient _kubeApi;
+
+ public KubernetesServiceDiscoveryProvider(KubeRegistryConfiguration kubeRegistryConfiguration, IOcelotLoggerFactory factory, IKubeApiClient kubeApi)
+ {
+ _kubeRegistryConfiguration = kubeRegistryConfiguration;
+ _logger = factory.CreateLogger();
+ _kubeApi = kubeApi;
+ }
+
+ public async Task> Get()
+ {
+ var endpoint = await _kubeApi
+ .ResourceClient(client => new EndPointClientV1(client))
+ .Get(_kubeRegistryConfiguration.KeyOfServiceInK8s, _kubeRegistryConfiguration.KubeNamespace);
+
+ var services = new List();
+ if (endpoint != null && endpoint.Subsets.Any())
+ {
+ services.AddRange(BuildServices(endpoint));
+ }
+ 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");
+ }
+
+ return services;
+ }
+
+ private static List BuildServices(EndpointsV1 endpoint)
+ {
+ var services = new List();
+
+ foreach (var subset in endpoint.Subsets)
+ {
+ services.AddRange(subset.Addresses.Select(address => new Service(endpoint.Metadata.Name,
+ new ServiceHostAndPort(address.Ip, subset.Ports.First().Port),
+ endpoint.Metadata.Uid, string.Empty, Enumerable.Empty())));
+ }
+
+ return services;
+ }
+}
diff --git a/src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj b/src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj
index 980cd909a..a9ac561a6 100644
--- a/src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj
+++ b/src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj
@@ -1,43 +1,48 @@
-