diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644
index 00000000..e6944a97
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,29 @@
+version: 2.1
+jobs:
+ build:
+ docker:
+ - image: mijitt0m/ocelot-build:0.0.1
+ steps:
+ - checkout
+ - run: make build
+ release:
+ docker:
+ - image: mijitt0m/ocelot-build:0.0.1
+ steps:
+ - checkout
+ - run: make release
+workflows:
+ version: 2
+ master:
+ jobs:
+ - release:
+ filters:
+ branches:
+ only: master
+ pr:
+ jobs:
+ - build:
+ filters:
+ branches:
+ ignore:
+ - master
diff --git a/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md
similarity index 98%
rename from CODE_OF_CONDUCT.md
rename to .github/CODE_OF_CONDUCT.md
index 49a79976..ebf3d11d 100644
--- a/CODE_OF_CONDUCT.md
+++ b/.github/CODE_OF_CONDUCT.md
@@ -1,46 +1,46 @@
-# Contributor Covenant Code of Conduct
-
-## Our Pledge
-
-In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
-
-## Our Standards
-
-Examples of behavior that contributes to creating a positive environment include:
-
-* Using welcoming and inclusive language
-* Being respectful of differing viewpoints and experiences
-* Gracefully accepting constructive criticism
-* Focusing on what is best for the community
-* Showing empathy towards other community members
-
-Examples of unacceptable behavior by participants include:
-
-* The use of sexualized language or imagery and unwelcome sexual attention or advances
-* Trolling, insulting/derogatory comments, and personal or political attacks
-* Public or private harassment
-* Publishing others' private information, such as a physical or electronic address, without explicit permission
-* Other conduct which could reasonably be considered inappropriate in a professional setting
-
-## Our Responsibilities
-
-Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
-
-Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
-
-## Scope
-
-This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
-
-## Enforcement
-
-Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at tom@threemammals.co.uk. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
-
-Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
-
-## Attribution
-
-This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
-
-[homepage]: http://contributor-covenant.org
-[version]: http://contributor-covenant.org/version/1/4/
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at tom@threemammals.co.uk. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
+
+[homepage]: http://contributor-covenant.org
+[version]: http://contributor-covenant.org/version/1/4/
diff --git a/CONTRIBUTING.md b/.github/CONTRIBUTING.md
similarity index 98%
rename from CONTRIBUTING.md
rename to .github/CONTRIBUTING.md
index 5174ce46..f722c978 100644
--- a/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -1,9 +1,9 @@
-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 :)
-
-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 :)
+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 :)
+
+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 :)
diff --git a/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
similarity index 92%
rename from ISSUE_TEMPLATE.md
rename to .github/ISSUE_TEMPLATE.md
index ddb30a7c..e4d3765a 100644
--- a/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -1,17 +1,17 @@
-## Expected Behavior / New Feature
-
-
-## Actual Behavior / Motivation for New Feature
-
-
-## Steps to Reproduce the Problem
-
- 1.
- 1.
- 1.
-
-## Specifications
-
- - Version:
- - Platform:
- - Subsystem:
+## Expected Behavior / New Feature
+
+
+## Actual Behavior / Motivation for New Feature
+
+
+## Steps to Reproduce the Problem
+
+ 1.
+ 1.
+ 1.
+
+## Specifications
+
+ - Version:
+ - Platform:
+ - Subsystem:
diff --git a/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
similarity index 88%
rename from PULL_REQUEST_TEMPLATE.md
rename to .github/PULL_REQUEST_TEMPLATE.md
index 72a3b6fe..8f6923ea 100644
--- a/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,7 +1,7 @@
-Fixes / New Feature #
-
-## Proposed Changes
-
- -
- -
- -
+Fixes / New Feature #
+
+## Proposed Changes
+
+ -
+ -
+ -
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 813fa571..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-language: csharp
-os:
- - osx
- - linux
-
-# Ubuntu 14.04
-sudo: required
-dist: bionic
-
-# OS X 10.12
-osx_image: xcode9.4
-
-mono:
- - 6.0.0
-
-dotnet: 3.0.100
-
-before_install:
- - git fetch --unshallow # Travis always does a shallow clone, but GitVersion needs the full history including branches and tags
- - git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
- - git fetch origin
-
-script:
- - ./build.sh
-
-cache:
- directories:
- - .packages
- - tools/Addins
- - tools/gitreleasemanager
- - tools/GitVersion.CommandLine
diff --git a/Directory.Build.props b/Directory.Build.props
index 2def6a21..4fe1ec98 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,16 +1,15 @@
-
-
- latest
-
- git
- https://github.com/ThreeMammals/Ocelot
-
- true
-
- true
- snupkg
-
-
-
-
-
+
+
+ latest
+ git
+ https://github.com/ThreeMammals/Ocelot
+
+ true
+
+ true
+ snupkg
+
+
+
+
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..a24d2a92
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,20 @@
+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 088d7be0..60369059 100644
--- a/Ocelot.sln
+++ b/Ocelot.sln
@@ -1,146 +1,219 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.27130.2036
-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
- .gitignore = .gitignore
- build-and-release-unstable.ps1 = build-and-release-unstable.ps1
- build-and-run-tests.ps1 = build-and-run-tests.ps1
- build.cake = build.cake
- build.ps1 = build.ps1
- codeanalysis.ruleset = codeanalysis.ruleset
- docker-compose.yaml = docker-compose.yaml
- Dockerfile = Dockerfile
- GitVersion.yml = GitVersion.yml
- global.json = global.json
- LICENSE.md = LICENSE.md
- README.md = README.md
- release.ps1 = release.ps1
- ReleaseNotes.md = ReleaseNotes.md
- run-acceptance-tests.ps1 = run-acceptance-tests.ps1
- run-benchmarks.ps1 = run-benchmarks.ps1
- run-unit-tests.ps1 = run-unit-tests.ps1
- version.ps1 = version.ps1
- EndProjectSection
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{5B401523-36DA-4491-B73A-7590A26E420B}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot", "src\Ocelot\Ocelot.csproj", "{D6DF4206-0DBA-41D8-884D-C3E08290FDBB}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.UnitTests", "test\Ocelot.UnitTests\Ocelot.UnitTests.csproj", "{54E84F1A-E525-4443-96EC-039CBD50C263}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.AcceptanceTests", "test\Ocelot.AcceptanceTests\Ocelot.AcceptanceTests.csproj", "{F8C224FE-36BE-45F5-9B0E-666D8F4A9B52}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.ManualTest", "test\Ocelot.ManualTest\Ocelot.ManualTest.csproj", "{02BBF4C5-517E-4157-8D21-4B8B9E118B7A}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Benchmarks", "test\Ocelot.Benchmarks\Ocelot.Benchmarks.csproj", "{106B49E6-95F6-4A7B-B81C-96BFA74AF035}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.IntegrationTests", "test\Ocelot.IntegrationTests\Ocelot.IntegrationTests.csproj", "{D4575572-99CA-4530-8737-C296EDA326F8}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Administration", "src\Ocelot.Administration\Ocelot.Administration.csproj", "{F69CEF43-27D2-4940-A47A-FCA879E371BC}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Cache.CacheManager", "src\Ocelot.Cache.CacheManager\Ocelot.Cache.CacheManager.csproj", "{EB9F438F-062E-499F-B6EA-4412BEF6D74C}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Consul", "src\Ocelot.Provider.Consul\Ocelot.Provider.Consul.csproj", "{02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Eureka", "src\Ocelot.Provider.Eureka\Ocelot.Provider.Eureka.csproj", "{9BBD3586-145C-4FA0-91C5-9ED58287D753}"
-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("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ocelot.Provider.Kubernetes", "src\Ocelot.Provider.Kubernetes\Ocelot.Provider.Kubernetes.csproj", "{72C8E528-B4F5-45CE-8A06-CD3787364856}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Release|Any CPU.Build.0 = Release|Any CPU
- {54E84F1A-E525-4443-96EC-039CBD50C263}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {54E84F1A-E525-4443-96EC-039CBD50C263}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {54E84F1A-E525-4443-96EC-039CBD50C263}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {54E84F1A-E525-4443-96EC-039CBD50C263}.Release|Any CPU.Build.0 = Release|Any CPU
- {F8C224FE-36BE-45F5-9B0E-666D8F4A9B52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F8C224FE-36BE-45F5-9B0E-666D8F4A9B52}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F8C224FE-36BE-45F5-9B0E-666D8F4A9B52}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F8C224FE-36BE-45F5-9B0E-666D8F4A9B52}.Release|Any CPU.Build.0 = Release|Any CPU
- {02BBF4C5-517E-4157-8D21-4B8B9E118B7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {02BBF4C5-517E-4157-8D21-4B8B9E118B7A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {02BBF4C5-517E-4157-8D21-4B8B9E118B7A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {02BBF4C5-517E-4157-8D21-4B8B9E118B7A}.Release|Any CPU.Build.0 = Release|Any CPU
- {106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Release|Any CPU.Build.0 = Release|Any CPU
- {D4575572-99CA-4530-8737-C296EDA326F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D4575572-99CA-4530-8737-C296EDA326F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D4575572-99CA-4530-8737-C296EDA326F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D4575572-99CA-4530-8737-C296EDA326F8}.Release|Any CPU.Build.0 = Release|Any CPU
- {F69CEF43-27D2-4940-A47A-FCA879E371BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F69CEF43-27D2-4940-A47A-FCA879E371BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F69CEF43-27D2-4940-A47A-FCA879E371BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F69CEF43-27D2-4940-A47A-FCA879E371BC}.Release|Any CPU.Build.0 = Release|Any CPU
- {EB9F438F-062E-499F-B6EA-4412BEF6D74C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {EB9F438F-062E-499F-B6EA-4412BEF6D74C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {EB9F438F-062E-499F-B6EA-4412BEF6D74C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {EB9F438F-062E-499F-B6EA-4412BEF6D74C}.Release|Any CPU.Build.0 = Release|Any CPU
- {02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0}.Release|Any CPU.Build.0 = Release|Any CPU
- {9BBD3586-145C-4FA0-91C5-9ED58287D753}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {9BBD3586-145C-4FA0-91C5-9ED58287D753}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {9BBD3586-145C-4FA0-91C5-9ED58287D753}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {9BBD3586-145C-4FA0-91C5-9ED58287D753}.Release|Any CPU.Build.0 = Release|Any CPU
- {1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {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
- {6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Release|Any CPU.Build.0 = Release|Any CPU
- {72C8E528-B4F5-45CE-8A06-CD3787364856}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {72C8E528-B4F5-45CE-8A06-CD3787364856}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {72C8E528-B4F5-45CE-8A06-CD3787364856}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {72C8E528-B4F5-45CE-8A06-CD3787364856}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(NestedProjects) = preSolution
- {D6DF4206-0DBA-41D8-884D-C3E08290FDBB} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
- {54E84F1A-E525-4443-96EC-039CBD50C263} = {5B401523-36DA-4491-B73A-7590A26E420B}
- {F8C224FE-36BE-45F5-9B0E-666D8F4A9B52} = {5B401523-36DA-4491-B73A-7590A26E420B}
- {02BBF4C5-517E-4157-8D21-4B8B9E118B7A} = {5B401523-36DA-4491-B73A-7590A26E420B}
- {106B49E6-95F6-4A7B-B81C-96BFA74AF035} = {5B401523-36DA-4491-B73A-7590A26E420B}
- {D4575572-99CA-4530-8737-C296EDA326F8} = {5B401523-36DA-4491-B73A-7590A26E420B}
- {F69CEF43-27D2-4940-A47A-FCA879E371BC} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
- {EB9F438F-062E-499F-B6EA-4412BEF6D74C} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
- {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}
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {21476EFF-778A-4F97-8A56-D1AF1CEC0C48}
- EndGlobalSection
-EndGlobal
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29613.14
+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
+ .gitignore = .gitignore
+ build.cake = build.cake
+ build.ps1 = build.ps1
+ codeanalysis.ruleset = codeanalysis.ruleset
+ GitVersion.yml = GitVersion.yml
+ LICENSE.md = LICENSE.md
+ README.md = README.md
+ ReleaseNotes.md = ReleaseNotes.md
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{5B401523-36DA-4491-B73A-7590A26E420B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot", "src\Ocelot\Ocelot.csproj", "{D6DF4206-0DBA-41D8-884D-C3E08290FDBB}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.UnitTests", "test\Ocelot.UnitTests\Ocelot.UnitTests.csproj", "{54E84F1A-E525-4443-96EC-039CBD50C263}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.AcceptanceTests", "test\Ocelot.AcceptanceTests\Ocelot.AcceptanceTests.csproj", "{F8C224FE-36BE-45F5-9B0E-666D8F4A9B52}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.ManualTest", "test\Ocelot.ManualTest\Ocelot.ManualTest.csproj", "{02BBF4C5-517E-4157-8D21-4B8B9E118B7A}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Benchmarks", "test\Ocelot.Benchmarks\Ocelot.Benchmarks.csproj", "{106B49E6-95F6-4A7B-B81C-96BFA74AF035}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.IntegrationTests", "test\Ocelot.IntegrationTests\Ocelot.IntegrationTests.csproj", "{D4575572-99CA-4530-8737-C296EDA326F8}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Administration", "src\Ocelot.Administration\Ocelot.Administration.csproj", "{F69CEF43-27D2-4940-A47A-FCA879E371BC}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Cache.CacheManager", "src\Ocelot.Cache.CacheManager\Ocelot.Cache.CacheManager.csproj", "{EB9F438F-062E-499F-B6EA-4412BEF6D74C}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Consul", "src\Ocelot.Provider.Consul\Ocelot.Provider.Consul.csproj", "{02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Eureka", "src\Ocelot.Provider.Eureka\Ocelot.Provider.Eureka.csproj", "{9BBD3586-145C-4FA0-91C5-9ED58287D753}"
+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}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AdministrationApi", "samples\AdministrationApi\AdministrationApi.csproj", "{B180F8AE-2F8F-44F9-9E5D-FE65B84B742E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OcelotGraphQL", "samples\OcelotGraphQL\OcelotGraphQL.csproj", "{F43429C3-EC49-464F-9423-9118A36E8FE3}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "eureka", "eureka", "{F1CF6F06-5A34-4A6A-8C19-003A78AB0DCF}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApiGateway", "samples\OcelotEureka\ApiGateway\ApiGateway.csproj", "{48B3DD3C-7F4D-40C1-A104-3BF9EF4ACE29}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DownstreamService", "samples\OcelotEureka\DownstreamService\DownstreamService.csproj", "{32ADF9B3-CBFA-4607-8A8E-1532D90A7197}"
+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}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DownstreamService", "samples\OcelotKube\DownstreamService\DownstreamService.csproj", "{7B319B8C-8155-4779-BD93-5ABD05CA2AB6}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "service-fabric", "service-fabric", "{B412628F-C325-47E1-A8D9-873DE04C8AF5}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OcelotApplicationApiGateway", "samples\OcelotServiceFabric\src\OcelotApplicationApiGateway\OcelotApplicationApiGateway.csproj", "{8E6DAE6E-E9B1-433A-80C3-1E2640FBA590}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OcelotApplicationService", "samples\OcelotServiceFabric\src\OcelotApplicationService\OcelotApplicationService.csproj", "{33BE6D88-F188-4E60-83AC-3C4B94D24675}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "administration", "administration", "{1F1F324D-6EA4-4E63-A6A7-C6053F412F1A}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "basic", "basic", "{ED066001-BAF7-4117-9884-DF591A56347D}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "graphql", "graphql", "{C15CD120-5F8D-41DE-9B21-00E3EA77D6C1}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {54E84F1A-E525-4443-96EC-039CBD50C263}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {54E84F1A-E525-4443-96EC-039CBD50C263}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {54E84F1A-E525-4443-96EC-039CBD50C263}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {54E84F1A-E525-4443-96EC-039CBD50C263}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F8C224FE-36BE-45F5-9B0E-666D8F4A9B52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F8C224FE-36BE-45F5-9B0E-666D8F4A9B52}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F8C224FE-36BE-45F5-9B0E-666D8F4A9B52}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F8C224FE-36BE-45F5-9B0E-666D8F4A9B52}.Release|Any CPU.Build.0 = Release|Any CPU
+ {02BBF4C5-517E-4157-8D21-4B8B9E118B7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {02BBF4C5-517E-4157-8D21-4B8B9E118B7A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {02BBF4C5-517E-4157-8D21-4B8B9E118B7A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {02BBF4C5-517E-4157-8D21-4B8B9E118B7A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D4575572-99CA-4530-8737-C296EDA326F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D4575572-99CA-4530-8737-C296EDA326F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D4575572-99CA-4530-8737-C296EDA326F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D4575572-99CA-4530-8737-C296EDA326F8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F69CEF43-27D2-4940-A47A-FCA879E371BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F69CEF43-27D2-4940-A47A-FCA879E371BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F69CEF43-27D2-4940-A47A-FCA879E371BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F69CEF43-27D2-4940-A47A-FCA879E371BC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EB9F438F-062E-499F-B6EA-4412BEF6D74C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EB9F438F-062E-499F-B6EA-4412BEF6D74C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EB9F438F-062E-499F-B6EA-4412BEF6D74C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EB9F438F-062E-499F-B6EA-4412BEF6D74C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9BBD3586-145C-4FA0-91C5-9ED58287D753}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9BBD3586-145C-4FA0-91C5-9ED58287D753}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9BBD3586-145C-4FA0-91C5-9ED58287D753}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9BBD3586-145C-4FA0-91C5-9ED58287D753}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {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
+ {6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {72C8E528-B4F5-45CE-8A06-CD3787364856}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {72C8E528-B4F5-45CE-8A06-CD3787364856}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {72C8E528-B4F5-45CE-8A06-CD3787364856}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {72C8E528-B4F5-45CE-8A06-CD3787364856}.Release|Any CPU.Build.0 = Release|Any CPU
+ {ED0B3A09-112B-4BA4-82D6-11569BC7A99B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {ED0B3A09-112B-4BA4-82D6-11569BC7A99B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {ED0B3A09-112B-4BA4-82D6-11569BC7A99B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {ED0B3A09-112B-4BA4-82D6-11569BC7A99B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B180F8AE-2F8F-44F9-9E5D-FE65B84B742E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B180F8AE-2F8F-44F9-9E5D-FE65B84B742E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B180F8AE-2F8F-44F9-9E5D-FE65B84B742E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B180F8AE-2F8F-44F9-9E5D-FE65B84B742E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F43429C3-EC49-464F-9423-9118A36E8FE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F43429C3-EC49-464F-9423-9118A36E8FE3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F43429C3-EC49-464F-9423-9118A36E8FE3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F43429C3-EC49-464F-9423-9118A36E8FE3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {48B3DD3C-7F4D-40C1-A104-3BF9EF4ACE29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {48B3DD3C-7F4D-40C1-A104-3BF9EF4ACE29}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {48B3DD3C-7F4D-40C1-A104-3BF9EF4ACE29}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {48B3DD3C-7F4D-40C1-A104-3BF9EF4ACE29}.Release|Any CPU.Build.0 = Release|Any CPU
+ {32ADF9B3-CBFA-4607-8A8E-1532D90A7197}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {32ADF9B3-CBFA-4607-8A8E-1532D90A7197}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {32ADF9B3-CBFA-4607-8A8E-1532D90A7197}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {32ADF9B3-CBFA-4607-8A8E-1532D90A7197}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8500055B-2C51-4CF1-A6EE-F05BB3E9BF16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8500055B-2C51-4CF1-A6EE-F05BB3E9BF16}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8500055B-2C51-4CF1-A6EE-F05BB3E9BF16}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8500055B-2C51-4CF1-A6EE-F05BB3E9BF16}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7B319B8C-8155-4779-BD93-5ABD05CA2AB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7B319B8C-8155-4779-BD93-5ABD05CA2AB6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7B319B8C-8155-4779-BD93-5ABD05CA2AB6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7B319B8C-8155-4779-BD93-5ABD05CA2AB6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8E6DAE6E-E9B1-433A-80C3-1E2640FBA590}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8E6DAE6E-E9B1-433A-80C3-1E2640FBA590}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8E6DAE6E-E9B1-433A-80C3-1E2640FBA590}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8E6DAE6E-E9B1-433A-80C3-1E2640FBA590}.Release|Any CPU.Build.0 = Release|Any CPU
+ {33BE6D88-F188-4E60-83AC-3C4B94D24675}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {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
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {D6DF4206-0DBA-41D8-884D-C3E08290FDBB} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
+ {54E84F1A-E525-4443-96EC-039CBD50C263} = {5B401523-36DA-4491-B73A-7590A26E420B}
+ {F8C224FE-36BE-45F5-9B0E-666D8F4A9B52} = {5B401523-36DA-4491-B73A-7590A26E420B}
+ {02BBF4C5-517E-4157-8D21-4B8B9E118B7A} = {5B401523-36DA-4491-B73A-7590A26E420B}
+ {106B49E6-95F6-4A7B-B81C-96BFA74AF035} = {5B401523-36DA-4491-B73A-7590A26E420B}
+ {D4575572-99CA-4530-8737-C296EDA326F8} = {5B401523-36DA-4491-B73A-7590A26E420B}
+ {F69CEF43-27D2-4940-A47A-FCA879E371BC} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
+ {EB9F438F-062E-499F-B6EA-4412BEF6D74C} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
+ {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}
+ {B180F8AE-2F8F-44F9-9E5D-FE65B84B742E} = {1F1F324D-6EA4-4E63-A6A7-C6053F412F1A}
+ {F43429C3-EC49-464F-9423-9118A36E8FE3} = {C15CD120-5F8D-41DE-9B21-00E3EA77D6C1}
+ {F1CF6F06-5A34-4A6A-8C19-003A78AB0DCF} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
+ {48B3DD3C-7F4D-40C1-A104-3BF9EF4ACE29} = {F1CF6F06-5A34-4A6A-8C19-003A78AB0DCF}
+ {32ADF9B3-CBFA-4607-8A8E-1532D90A7197} = {F1CF6F06-5A34-4A6A-8C19-003A78AB0DCF}
+ {4B706988-4817-43A8-ABE1-32A67998C2C8} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
+ {8500055B-2C51-4CF1-A6EE-F05BB3E9BF16} = {4B706988-4817-43A8-ABE1-32A67998C2C8}
+ {7B319B8C-8155-4779-BD93-5ABD05CA2AB6} = {4B706988-4817-43A8-ABE1-32A67998C2C8}
+ {B412628F-C325-47E1-A8D9-873DE04C8AF5} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
+ {8E6DAE6E-E9B1-433A-80C3-1E2640FBA590} = {B412628F-C325-47E1-A8D9-873DE04C8AF5}
+ {33BE6D88-F188-4E60-83AC-3C4B94D24675} = {B412628F-C325-47E1-A8D9-873DE04C8AF5}
+ {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}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {21476EFF-778A-4F97-8A56-D1AF1CEC0C48}
+ EndGlobalSection
+EndGlobal
diff --git a/README.md b/README.md
index 6d26bee4..a655d3d2 100644
--- a/README.md
+++ b/README.md
@@ -1,102 +1,98 @@
-[
](https://threemammals.com/ocelot)
-
-[](https://ci.appveyor.com/project/TomPallister/ocelot-fcfpb) Windows (AppVeyor)
-
-[](https://travis-ci.org/ThreeMammals/Ocelot) Linux & OSX (Travis)
-
-[](https://ci.appveyor.com/project/TomPallister/ocelot-fcfpb/history?branch=develop)
-
-[](https://coveralls.io/github/ThreeMammals/Ocelot?branch=develop)
-
-
-
-# Ocelot
-
-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.
-
-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!
-
-## Features
-
-A quick list of Ocelot's capabilities for more information see the [documentation](https://ocelot.readthedocs.io/en/latest/).
-
-* Routing
-* Request Aggregation
-* Service Discovery with Consul & Eureka
-* Service Fabric
-* Kubernetes
-* WebSockets
-* Authentication
-* Authorisation
-* Rate Limiting
-* Caching
-* Retry policies / QoS
-* Load Balancing
-* Logging / Tracing / Correlation
-* Headers / 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 2.1` and `.NET Framework 4.7.2` 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.
-
-Install Ocelot and it's dependencies using NuGet.
-
-`Install-Package Ocelot`
-
-Or via the .NET Core CLI:
-
-`dotnet add package ocelot`
-
-All versions can be found [here](https://www.nuget.org/packages/Ocelot/)
-
-## Documentation
-
-Please click [here](https://ocelot.readthedocs.io/en/latest/) for the Ocelot documentation. This includes lots of information and will be helpful if you want to understand the features Ocelot currently offers.
-
-## Coming up
-
-You can see what we are working on [here](https://github.com/ThreeMammals/Ocelot/issues).
-
-## Contributing
-
-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 :)
-
-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!
-
-[](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)
+[
](https://threemammals.com/ocelot)
+
+[](https://circleci.com/gh/ThreeMammals/Ocelot/tree/master)
+
+[](https://coveralls.io/github/ThreeMammals/Ocelot?branch=master)
+
+[Slack](threemammals.slack.com)
+
+# Ocelot
+
+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.
+
+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!
+
+## Features
+
+A quick list of Ocelot's capabilities for more information see the [documentation](https://ocelot.readthedocs.io/en/latest/).
+
+* Routing
+* Request Aggregation
+* Service Discovery with Consul & Eureka
+* Service Fabric
+* Kubernetes
+* WebSockets
+* Authentication
+* Authorisation
+* Rate Limiting
+* Caching
+* Retry policies / QoS
+* Load Balancing
+* Logging / Tracing / Correlation
+* Headers / 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.
+
+Install Ocelot and it's dependencies using NuGet.
+
+`Install-Package Ocelot`
+
+Or via the .NET Core CLI:
+
+`dotnet add package ocelot`
+
+All versions can be found [here](https://www.nuget.org/packages/Ocelot/)
+
+## Documentation
+
+Please click [here](https://ocelot.readthedocs.io/en/latest/) for the Ocelot documentation. This includes lots of information and will be helpful if you want to understand the features Ocelot currently offers.
+
+## Coming up
+
+You can see what we are working on [here](https://github.com/ThreeMammals/Ocelot/issues).
+
+## Contributing
+
+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 :)
+
+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!
+
+[](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)
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index 6e79d909..00000000
--- a/appveyor.yml
+++ /dev/null
@@ -1 +0,0 @@
-image: Visual Studio 2019
\ No newline at end of file
diff --git a/build-and-release-unstable.ps1 b/build-and-release-unstable.ps1
deleted file mode 100644
index 269a8689..00000000
--- a/build-and-release-unstable.ps1
+++ /dev/null
@@ -1,2 +0,0 @@
-./build.ps1 -target BuildAndReleaseUnstable
-exit $LASTEXITCODE
\ No newline at end of file
diff --git a/build-and-run-tests.ps1 b/build-and-run-tests.ps1
deleted file mode 100644
index 6ff61c0a..00000000
--- a/build-and-run-tests.ps1
+++ /dev/null
@@ -1,2 +0,0 @@
-./build.ps1 -target RunTests
-exit $LASTEXITCODE
\ No newline at end of file
diff --git a/build.cake b/build.cake
index 5eb3d77c..3d4626bd 100644
--- a/build.cake
+++ b/build.cake
@@ -1,13 +1,15 @@
-#tool "nuget:?package=GitVersion.CommandLine"
+#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
@@ -17,8 +19,8 @@ var artifactsDir = Directory("artifacts");
var artifactsForUnitTestsDir = artifactsDir + Directory("UnitTests");
var unitTestAssemblies = @"./test/Ocelot.UnitTests/Ocelot.UnitTests.csproj";
var minCodeCoverage = 80d;
-var coverallsRepoToken = "coveralls-repo-token-ocelot";
-var coverallsRepo = "https://coveralls.io/github/TomPallister/Ocelot";
+var coverallsRepoToken = "OCELOT_COVERALLS_TOKEN";
+var coverallsRepo = "https://coveralls.io/github/ThreeMammals/Ocelot";
// acceptance testing
var artifactsForAcceptanceTestsDir = artifactsDir + Directory("AcceptanceTests");
@@ -37,41 +39,54 @@ var packagesDir = artifactsDir + Directory("Packages");
var releaseNotesFile = packagesDir + File("releasenotes.md");
var artifactsFile = packagesDir + File("artifacts.txt");
-// unstable releases
-var nugetFeedUnstableKey = EnvironmentVariable("nuget-apikey-unstable");
-var nugetFeedUnstableUploadUrl = "https://www.nuget.org/api/v2/package";
-var nugetFeedUnstableSymbolsUploadUrl = "https://www.nuget.org/api/v2/package";
-
// stable releases
-var tagsUrl = "https://api.github.com/repos/tompallister/ocelot/releases/tags/";
-var nugetFeedStableKey = EnvironmentVariable("nuget-apikey-stable");
+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.
-var releaseTag = "";
string committedVersion = "0.0.0-dev";
-var buildVersion = committedVersion;
GitVersion versioning = null;
-var nugetFeedUnstableBranchFilter = "^(develop)$|^(PullRequest/)";
+int releaseId = 0;
+string gitHubUsername = "TomPallister";
+string gitHubPassword = Environment.GetEnvironmentVariable("OCELOT_GITHUB_API_KEY");
var target = Argument("target", "Default");
-
-Information("target is " +target);
+Information("target is " + target);
Information("Build configuration is " + compileConfig);
Task("Default")
.IsDependentOn("Build");
Task("Build")
- .IsDependentOn("RunTests")
- .IsDependentOn("CreatePackages");
+ .IsDependentOn("RunTests");
-Task("BuildAndReleaseUnstable")
+Task("RunTests")
+ .IsDependentOn("RunUnitTests")
+ .IsDependentOn("RunAcceptanceTests")
+ .IsDependentOn("RunIntegrationTests");
+
+Task("Release")
.IsDependentOn("Build")
- .IsDependentOn("ReleasePackagesToUnstableFeed");
-
+ .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(() =>
{
@@ -89,11 +104,10 @@ Task("Version")
var nugetVersion = versioning.NuGetVersion;
Information("SemVer version number: " + nugetVersion);
- if (AppVeyor.IsRunningOnAppVeyor)
+ if (IsRunningOnCircleCI())
{
Information("Persisting version number...");
PersistVersion(committedVersion, nugetVersion);
- buildVersion = nugetVersion;
}
else
{
@@ -101,19 +115,6 @@ Task("Version")
}
});
-Task("Compile")
- .IsDependentOn("Clean")
- .IsDependentOn("Version")
- .Does(() =>
- {
- var settings = new DotNetCoreBuildSettings
- {
- Configuration = compileConfig,
- };
-
- DotNetCoreBuild(slnFile, settings);
- });
-
Task("RunUnitTests")
.IsDependentOn("Compile")
.Does(() =>
@@ -123,71 +124,55 @@ Task("RunUnitTests")
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);
-
- if (IsRunningOnWindows())
+
+ 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 coverageSummaryFile = GetSubDirectories(artifactsForUnitTestsDir).First().CombineWithFilePath(File("coverage.opencover.xml"));
- ReportGenerator(coverageSummaryFile, artifactsForUnitTestsDir);
-
- if (AppVeyor.IsRunningOnAppVeyor)
+ var repoToken = EnvironmentVariable(coverallsRepoToken);
+ if (string.IsNullOrEmpty(repoToken))
{
- 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");
+ throw new Exception(string.Format("Coveralls repo token not found. Set environment variable '{0}'", coverallsRepoToken));
}
- var sequenceCoverage = XmlPeek(coverageSummaryFile, "//CoverageSession/Summary/@sequenceCoverage");
- var branchCoverage = XmlPeek(coverageSummaryFile, "//CoverageSession/Summary/@branchCoverage");
-
- Information("Sequence Coverage: " + sequenceCoverage);
-
- if(double.Parse(sequenceCoverage) < minCodeCoverage)
+ Information(string.Format("Uploading test coverage to {0}", coverallsRepo));
+ CoverallsNet(coverageSummaryFile, CoverallsNetReportType.OpenCover, new CoverallsNetSettings()
{
- var whereToCheck = !AppVeyor.IsRunningOnAppVeyor ? 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));
- };
+ 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(() =>
{
- if(TravisCI.IsRunningOnTravisCI)
- {
- Information(
- @"Job:
- JobId: {0}
- JobNumber: {1}
- OSName: {2}",
- BuildSystem.TravisCI.Environment.Job.JobId,
- BuildSystem.TravisCI.Environment.Job.JobNumber,
- BuildSystem.TravisCI.Environment.Job.OSName
- );
-
- if(TravisCI.Environment.Job.OSName.ToLower() == "osx")
- {
- return;
- }
- }
-
var settings = new DotNetCoreTestSettings
{
Configuration = compileConfig,
@@ -204,24 +189,6 @@ Task("RunIntegrationTests")
.IsDependentOn("Compile")
.Does(() =>
{
- if(TravisCI.IsRunningOnTravisCI)
- {
- Information(
- @"Job:
- JobId: {0}
- JobNumber: {1}
- OSName: {2}",
- BuildSystem.TravisCI.Environment.Job.JobId,
- BuildSystem.TravisCI.Environment.Job.JobNumber,
- BuildSystem.TravisCI.Environment.Job.OSName
- );
-
- if(TravisCI.Environment.Job.OSName.ToLower() == "osx")
- {
- return;
- }
- }
-
var settings = new DotNetCoreTestSettings
{
Configuration = compileConfig,
@@ -234,12 +201,7 @@ Task("RunIntegrationTests")
DotNetCoreTest(integrationTestAssemblies, settings);
});
-Task("RunTests")
- .IsDependentOn("RunUnitTests")
- .IsDependentOn("RunAcceptanceTests")
- .IsDependentOn("RunIntegrationTests");
-
-Task("CreatePackages")
+Task("CreateArtifacts")
.IsDependentOn("Compile")
.Does(() =>
{
@@ -247,6 +209,7 @@ Task("CreatePackages")
CopyFiles("./src/**/Release/Ocelot.*.nupkg", packagesDir);
+ // todo fix this for docker build
//GenerateReleaseNotes(releaseNotesFile);
var projectFiles = GetFiles("./src/**/Release/Ocelot.*.nupkg");
@@ -255,6 +218,7 @@ Task("CreatePackages")
{
System.IO.File.AppendAllLines(artifactsFile, new[]{
projectFile.GetFilename().FullPath,
+ // todo fix this for docker build
//"releaseNotes:releasenotes.md"
});
}
@@ -269,101 +233,60 @@ Task("CreatePackages")
Information("Created package " + codePackage);
}
+ });
- if (AppVeyor.IsRunningOnAppVeyor)
+Task("PublishGitHubRelease")
+ .IsDependentOn("CreateArtifacts")
+ .Does(() =>
+ {
+ if (IsRunningOnCircleCI())
{
var path = packagesDir.ToString() + @"/**/*";
+ CreateGitHubRelease();
+
foreach (var file in GetFiles(path))
{
- AppVeyor.UploadArtifact(file.FullPath);
+ UploadFileToGitHubRelease(file);
}
- }
- });
-Task("ReleasePackagesToUnstableFeed")
- .IsDependentOn("CreatePackages")
- .Does(() =>
- {
- if (ShouldPublishToUnstableFeed(nugetFeedUnstableBranchFilter, versioning.BranchName))
- {
- PublishPackages(packagesDir, artifactsFile, nugetFeedUnstableKey, nugetFeedUnstableUploadUrl, nugetFeedUnstableSymbolsUploadUrl);
+ CompleteGitHubRelease();
}
});
Task("EnsureStableReleaseRequirements")
- .Does(() =>
+ .Does(() =>
{
Information("Check if stable release...");
- if (!AppVeyor.IsRunningOnAppVeyor)
+ if (!IsRunningOnCircleCI())
{
- throw new Exception("Stable release should happen via appveyor");
- }
-
- Information("Running on AppVeyor...");
-
- Information("IsTag = " + AppVeyor.Environment.Repository.Tag.IsTag);
-
- Information("Name = " + AppVeyor.Environment.Repository.Tag.Name);
-
- var isTag =
- AppVeyor.Environment.Repository.Tag.IsTag &&
- !string.IsNullOrWhiteSpace(AppVeyor.Environment.Repository.Tag.Name);
-
- if (!isTag)
- {
- throw new Exception("Stable release should happen from a published GitHub release");
+ throw new Exception("Stable release should happen via circleci");
}
Information("Release is stable...");
});
-Task("UpdateVersionInfo")
- .IsDependentOn("EnsureStableReleaseRequirements")
- .Does(() =>
- {
- releaseTag = AppVeyor.Environment.Repository.Tag.Name;
- AppVeyor.UpdateBuildVersion(releaseTag);
- });
-
Task("DownloadGitHubReleaseArtifacts")
- .IsDependentOn("UpdateVersionInfo")
.Does(() =>
{
+
try
{
- Information("DownloadGitHubReleaseArtifacts");
-
EnsureDirectoryExists(packagesDir);
- Information("Directory exists...");
-
- var releaseUrl = tagsUrl + releaseTag;
-
- Information("Release url " + releaseUrl);
+ var releaseUrl = tagsUrl + versioning.NuGetVersion;
var assets_url = Newtonsoft.Json.Linq.JObject.Parse(GetResource(releaseUrl))
.Value("assets_url");
- Information("Assets url " + assets_url);
-
var assets = GetResource(assets_url);
- Information("Assets " + assets_url);
-
foreach(var asset in Newtonsoft.Json.JsonConvert.DeserializeObject(assets))
{
- Information("In the loop..");
-
var file = packagesDir + File(asset.Value("name"));
-
- Information("Downloading " + file);
-
DownloadFile(asset.Value("browser_download_url"), file);
}
-
- Information("Out of the loop...");
}
catch(Exception exception)
{
@@ -372,16 +295,16 @@ Task("DownloadGitHubReleaseArtifacts")
}
});
-Task("ReleasePackagesToStableFeed")
+Task("PublishToNuget")
.IsDependentOn("DownloadGitHubReleaseArtifacts")
.Does(() =>
{
- PublishPackages(packagesDir, artifactsFile, nugetFeedStableKey, nugetFeedStableUploadUrl, nugetFeedStableSymbolsUploadUrl);
+ if (IsRunningOnCircleCI())
+ {
+ PublishPackages(packagesDir, artifactsFile, nugetFeedStableKey, nugetFeedStableUploadUrl, nugetFeedStableSymbolsUploadUrl);
+ }
});
-Task("Release")
- .IsDependentOn("ReleasePackagesToStableFeed");
-
RunTarget(target);
/// Gets nuique nuget version for this commit
@@ -444,6 +367,7 @@ private void GenerateReleaseNotes(ConvertableFilePath file)
/// 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();
@@ -465,6 +389,81 @@ private void PublishPackages(ConvertableDirectoryPath packagesDir, ConvertableFi
}
}
+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)
{
@@ -495,17 +494,7 @@ private string GetResource(string url)
}
}
-private bool ShouldPublishToUnstableFeed(string filter, string branchName)
+private bool IsRunningOnCircleCI()
{
- var regex = new System.Text.RegularExpressions.Regex(filter);
- var publish = regex.IsMatch(branchName);
- if (publish)
- {
- Information("Branch " + branchName + " will be published to the unstable feed");
- }
- else
- {
- Information("Branch " + branchName + " will not be published to the unstable feed");
- }
- return publish;
-}
+ return !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("CIRCLECI"));
+}
\ No newline at end of file
diff --git a/build.ps1 b/build.ps1
index c6c91b25..a336e298 100644
--- a/build.ps1
+++ b/build.ps1
@@ -59,7 +59,10 @@ try {
# 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).
- [System.Net.ServicePointManager]::SecurityProtocol = 3072 -bor 768 -bor 192 -bor 48
+ # 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'
}
@@ -118,7 +121,7 @@ $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
+ New-Item -Path $TOOLS_DIR -Type Directory | Out-Null
}
# Make sure that packages.config exist.
@@ -155,7 +158,12 @@ if (!(Test-Path $NUGET_EXE)) {
}
# Save nuget.exe path to environment to be available to child processed
-$ENV:NUGET_EXE = $NUGET_EXE
+$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) {
@@ -163,16 +171,17 @@ if(-Not $SkipToolPackageRestore.IsPresent) {
Set-Location $TOOLS_DIR
# Check for changes in packages.config and remove installed tools if true.
- [string] $md5Hash = MD5HashFile($PACKAGES_CONFIG)
+ [string] $md5Hash = MD5HashFile $PACKAGES_CONFIG
if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or
- ($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) {
+ ($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
+ Remove-Item -Recurse -Force
}
Write-Verbose -Message "Restoring tools from NuGet..."
- $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`""
+
+ $NuGetOutput = Invoke-Expression "& $env:NUGET_EXE_INVOCATION install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`""
if ($LASTEXITCODE -ne 0) {
Throw "An error occurred while restoring NuGet tools."
@@ -181,7 +190,7 @@ if(-Not $SkipToolPackageRestore.IsPresent) {
{
$md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII"
}
- Write-Verbose -Message ($NuGetOutput | out-string)
+ Write-Verbose -Message ($NuGetOutput | Out-String)
Pop-Location
}
@@ -192,13 +201,13 @@ if (Test-Path $ADDINS_PACKAGES_CONFIG) {
Set-Location $ADDINS_DIR
Write-Verbose -Message "Restoring addins from NuGet..."
- $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$ADDINS_DIR`""
+ $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)
+ Write-Verbose -Message ($NuGetOutput | Out-String)
Pop-Location
}
@@ -209,13 +218,13 @@ if (Test-Path $MODULES_PACKAGES_CONFIG) {
Set-Location $MODULES_DIR
Write-Verbose -Message "Restoring modules from NuGet..."
- $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$MODULES_DIR`""
+ $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)
+ Write-Verbose -Message ($NuGetOutput | Out-String)
Pop-Location
}
@@ -225,11 +234,16 @@ 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 Cake arguments
-$cakeArguments = @("$Script");
-if ($Target) { $cakeArguments += "-target=$Target" }
+ # 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" }
@@ -238,5 +252,5 @@ $cakeArguments += $ScriptArgs
# Start Cake
Write-Host "Running build script..."
-&$CAKE_EXE $cakeArguments
+Invoke-Expression "& $CAKE_EXE_INVOCATION $($cakeArguments -join " ")"
exit $LASTEXITCODE
diff --git a/docker/Dockerfile.base b/docker/Dockerfile.base
new file mode 100644
index 00000000..02d91f53
--- /dev/null
+++ b/docker/Dockerfile.base
@@ -0,0 +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
diff --git a/docker/Dockerfile.build b/docker/Dockerfile.build
new file mode 100644
index 00000000..0478da41
--- /dev/null
+++ b/docker/Dockerfile.build
@@ -0,0 +1,15 @@
+# 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
diff --git a/docker/Dockerfile.release b/docker/Dockerfile.release
new file mode 100644
index 00000000..5d63816d
--- /dev/null
+++ b/docker/Dockerfile.release
@@ -0,0 +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
diff --git a/docker/README.md b/docker/README.md
new file mode 100644
index 00000000..3eb46aa4
--- /dev/null
+++ b/docker/README.md
@@ -0,0 +1,3 @@
+# docker build
+
+This folder contains the dockerfile and script to create the ocelot build container.
\ No newline at end of file
diff --git a/docker/docker-build.sh b/docker/docker-build.sh
new file mode 100755
index 00000000..8bb4e206
--- /dev/null
+++ b/docker/docker-build.sh
@@ -0,0 +1,6 @@
+# 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 8fbd37b9..0a494f9f 100644
--- a/docs/building/building.rst
+++ b/docs/building/building.rst
@@ -1,12 +1,10 @@
Building
========
-* You'll generally want to run the `./build.ps1` script. 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 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 view the current commit's `SemVer `_ build information by running `./version.ps1`.
+* 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 other `./*.ps1` scripts perform subsets of the build process, if you don't want to run the full build.
+* 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.
-* The release process works best with GitFlow branching; this allows us to publish every development commit to an unstable feed with a unique SemVer version, and then choose when to release to a stable feed.
-
-* Alternatively you can build the project in VS2017 with the latest .NET Core SDK.
\ No newline at end of file
+* Alternatively you can build the project in VS2019 with the latest .NET Core SDK.
\ No newline at end of file
diff --git a/docs/building/overview.rst b/docs/building/overview.rst
index a5977f49..70fdad0e 100644
--- a/docs/building/overview.rst
+++ b/docs/building/overview.rst
@@ -1,4 +1,4 @@
Overview
========
-This document summarises the build and release process for the project. The build scripts are written using `Cake `_, and are defined in `./build.cake`. The scripts have been designed to be run by either developers locally or by a build server (currently `AppVeyor `_), with minimal logic defined in the build server itself.
\ No newline at end of file
+This document summarises the build and release process for the project. The build scripts are written using `Cake `_, and are defined in `./build.cake`. The scripts have been designed to be run by either developers locally or by a build server (currently `CircleCi `_), with minimal logic defined in the build server itself.
\ No newline at end of file
diff --git a/docs/building/releaseprocess.rst b/docs/building/releaseprocess.rst
index b0b45571..43cb5f61 100644
--- a/docs/building/releaseprocess.rst
+++ b/docs/building/releaseprocess.rst
@@ -1,38 +1,35 @@
-Release process
-===============
-
-Ocelot uses the following process to accept work into the NuGet packages.
-
-1. User creates an issue or picks up an existing issue in GitHub.
-
-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 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?
- - Have I updated any documentation that my changes may have affected?
- - Does my feature make sense, have I checked all of Ocelot's other features to make sure it doesn't already exist?
-In order for a PR to be merged the following must have occured.
- - All new code is covered by unit tests.
- - All new code has at least 1 acceptance test covering the happy path.
- - Builds for Windows, Mac and Linux must have passed.
- - Builds for Windows, Mac and Linux 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 the GitHub issue must be labelled as merged. The merge will trigger an alpha build on the develop branch with these changes that will get pushed to NuGet. You can import this and test it manually should you wish.
-
-6. When the Ocelot team is ready to create a release they will checkout a new release branch with the version from the latest develop build. So look in AppVeyor for the latest develop semver number and checkout a new branch e.g. git checkout -b release/13.1.0 and push this to the remote. Wait for it to build and then merge this branch back into master.
-
-7. Wait for the master build to complete. When it has go to the AppVeyor UI and find the build and click the Deploy link in the top right hand corner. Select release preparation from the environment drop down and click deploy. This will send the release information to GitHub releases.
-
-8. Go to GitHub releases and find the version you have just released. It will look something like 13.0.0+31.build.1783. Remove +31.build.1783 (or whatever you get) from all the input fields so you are just left with 13.0.0. Untick This is a pre release and click release. This will trigger a build from AppVeyor that downloads the release artifacts from GitHub and publishes them to the stable NuGet feed. All being well you should find your new package on NuGet within 30 minutes. You might also want to manually add the issue numbers of what has been merged so people can see what changed in this release.
-
-9. The final step is to go back to GitHub and close any issues that were labelled as merged. You should see something like this in`GitHub `_ and this in `NuGet `_.
-
-Notes
------
-
-All NuGet package builds are done with AppVeyor `here _` and all releases are done from `here _`. We also build Ocelot on Travis `here `_ but this is only to make sure it is building on multiple OS.
-
-Only Ocelot core team members can merge PRs and create release branches. 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.
+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.
+
+Ocelot uses the following process to accept work into the NuGet packages.
+
+1. User creates an issue or picks up an existing issue in GitHub.
+
+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.
+
+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?
+ - Have I updated any documentation that my changes may have affected?
+ - Does my feature make sense, have I checked all of Ocelot's other features to make sure it doesn't already exist?
+In order for a PR to be merged the following must have occured.
+ - All new code is covered by unit tests.
+ - All new code has at least 1 acceptance test covering the happy path.
+ - Tests must have passed.
+ - 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.
+
+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 `_.
+
+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.
diff --git a/docs/features/caching.rst b/docs/features/caching.rst
index 4e692fff..c4be74f4 100644
--- a/docs/features/caching.rst
+++ b/docs/features/caching.rst
@@ -1,54 +1,54 @@
-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.
-
-The following example shows how to add CacheManager to Ocelot so that you can do output caching.
-
-First of all add the following NuGet package.
-
- ``Install-Package Ocelot.Cache.CacheManager``
-
-This will give you access to the Ocelot cache manager extension methods.
-
-The second thing you need to do something like the following to your ConfigureServices..
-
-.. code-block:: csharp
-
- s.AddOcelot()
- .AddCacheManager(x =>
- {
- x.WithDictionaryHandle();
- })
-
-Finally in order to use caching on a route in your ReRoute configuration add this setting.
-
-.. code-block:: json
-
- "FileCacheOptions": { "TtlSeconds": 15, "Region": "somename" }
-
-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.
-
-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>()``
-
-``IOcelotCache`` this is for output caching.
-
-``IOcelotCache`` this is for caching the file configuration if you are calling something remote to get your config such as Consul.
-
-Please dig into the Ocelot source code to find more. I would really appreciate it if anyone wants to implement Redis, memcache etc..
-
+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.
+
+The following example shows how to add CacheManager to Ocelot so that you can do output caching.
+
+First of all add the following NuGet package.
+
+ ``Install-Package Ocelot.Cache.CacheManager``
+
+This will give you access to the Ocelot cache manager extension methods.
+
+The second thing you need to do something like the following to your ConfigureServices..
+
+.. code-block:: csharp
+
+ s.AddOcelot()
+ .AddCacheManager(x =>
+ {
+ x.WithDictionaryHandle();
+ })
+
+Finally in order to use caching on a route in your ReRoute configuration add this setting.
+
+.. code-block:: json
+
+ "FileCacheOptions": { "TtlSeconds": 15, "Region": "somename" }
+
+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.
+
+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>()``
+
+``IOcelotCache`` this is for output caching.
+
+``IOcelotCache`` this is for caching the file configuration if you are calling something remote to get your config such as Consul.
+
+Please dig into the Ocelot source code to find more. I would really appreciate it if anyone wants to implement Redis, memcache etc..
+
diff --git a/docs/features/configuration.rst b/docs/features/configuration.rst
index 408eb450..7983af39 100644
--- a/docs/features/configuration.rst
+++ b/docs/features/configuration.rst
@@ -1,224 +1,230 @@
-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.
-
-.. code-block:: json
-
- {
- "ReRoutes": [],
- "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:
-
-.. code-block:: json
-
- {
- "DownstreamPathTemplate": "/",
- "UpstreamPathTemplate": "/",
- "UpstreamHttpMethod": [
- "Get"
- ],
- "AddHeadersToRequest": {},
- "AddClaimsToRequest": {},
- "RouteClaimsRequirement": {},
- "AddQueriesToRequest": {},
- "RequestIdKey": "",
- "FileCacheOptions": {
- "TtlSeconds": 0,
- "Region": ""
- },
- "ReRouteIsCaseSensitive": false,
- "ServiceName": "",
- "DownstreamScheme": "http",
- "DownstreamHostAndPorts": [
- {
- "Host": "localhost",
- "Port": 51876,
- }
- ],
- "QoSOptions": {
- "ExceptionsAllowedBeforeBreaking": 0,
- "DurationOfBreak": 0,
- "TimeoutValue": 0
- },
- "LoadBalancer": "",
- "RateLimitOptions": {
- "ClientWhitelist": [],
- "EnableRateLimiting": false,
- "Period": "",
- "PeriodTimespan": 0,
- "Limit": 0
- },
- "AuthenticationOptions": {
- "AuthenticationProviderKey": "",
- "AllowedScopes": []
- },
- "HttpHandlerOptions": {
- "AllowAutoRedirect": true,
- "UseCookieContainer": true,
- "UseTracing": true
- },
- "DangerousAcceptAnyServerCertificateValidator": false
- }
-
-More information on how to use these options is below..
-
-Multiple environments
-^^^^^^^^^^^^^^^^^^^^^
-
-Like any other asp.net core project Ocelot supports configuration file names such as configuration.dev.json, configuration.test.json etc. In order to implement this add the following
-to you
-
-.. code-block:: csharp
-
- .ConfigureAppConfiguration((hostingContext, config) =>
- {
- config
- .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
- .AddJsonFile("appsettings.json", true, true)
- .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
- .AddJsonFile("ocelot.json")
- .AddJsonFile($"configuration.{hostingContext.HostingEnvironment.EnvironmentName}.json")
- .AddEnvironmentVariables();
- })
-
-Ocelot will now use the environment specific configuration and fall back to ocelot.json if there isn't one.
-
-You also need to set the corresponding environment variable which is ASPNETCORE_ENVIRONMENT. More info on this can be found in the `asp.net core docs `_.
-
-Merging configuration files
-^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-This feature was requested in `Issue 296 `_ and allows users to have multiple configuration files to make managing large configurations easier.
-
-Instead of adding the configuration directly e.g. AddJsonFile("ocelot.json") you can call AddOcelot() like below.
-
-.. code-block:: csharp
-
- .ConfigureAppConfiguration((hostingContext, config) =>
- {
- config
- .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
- .AddJsonFile("appsettings.json", true, true)
- .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
- .AddOcelot(hostingContext.HostingEnvironment)
- .AddEnvironmentVariables();
- })
-
-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.
-
-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.
-
-You can also give Ocelot a specific path to look in for the configuration files like below.
-
-.. code-block:: csharp
-
- .ConfigureAppConfiguration((hostingContext, config) =>
- {
- config
- .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
- .AddJsonFile("appsettings.json", true, true)
- .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
- .AddOcelot("/foo/bar", hostingContext.HostingEnvironment)
- .AddEnvironmentVariables();
- })
-
-Ocelot needs the HostingEnvironment so it knows to exclude anything environment specific from the algorithm.
-
-Store configuration in 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 you add the following when you register your services Ocelot will attempt to store and retrieve its configuration in consul KV store.
-
-.. code-block:: csharp
-
- services
- .AddOcelot()
- .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.
-
-.. code-block:: json
-
- "GlobalConfiguration": {
- "ServiceDiscoveryProvider": {
- "Host": "localhost",
- "Port": 9500
- }
- }
-
-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.
-
-Reload JSON config on change
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Ocelot supports reloading the json configuration file on change. e.g. the following will recreate Ocelots internal configuration when the ocelot.json file is updated
-manually.
-
-.. code-block:: json
-
- config.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);
-
-Configuration Key
------------------
-
-If you are using Consul for configuration (or other providers in the future) you might want to key your configurations so you can have multiple configurations :) This feature was requested in `issue 346 `_! In order to specify the key you need to set the ConfigurationKey property in the ServiceDiscoveryProvider section of the configuration json file e.g.
-
-.. code-block:: json
-
- "GlobalConfiguration": {
- "ServiceDiscoveryProvider": {
- "Host": "localhost",
- "Port": 9500,
- "ConfigurationKey": "Oceolot_A"
- }
- }
-
-In this example Ocelot will use Oceolot_A as the key for your configuration when looking it up in Consul.
-
-If you do not set the ConfigurationKey Ocelot will use the string InternalConfiguration as the key.
-
-Follow Redirects / Use CookieContainer
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Use HttpHandlerOptions in ReRoute configuration to set up HttpHandler behavior:
-
-1. AllowAutoRedirect is a value that indicates whether the request should follow redirection responses. Set it true if the request should automatically
-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!
-
-SSL Errors
-^^^^^^^^^^
-
-If you want to ignore SSL warnings / errors set the following in your ReRoute config.
-
-.. code-block:: json
-
- "DangerousAcceptAnyServerCertificateValidator": true
-
-I don't recommend doing this, I suggest creating your own certificate and then getting it trusted by your local / remote machine if you can.
+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.
+
+.. code-block:: json
+
+ {
+ "ReRoutes": [],
+ "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:
+
+.. code-block:: json
+
+ {
+ "DownstreamPathTemplate": "/",
+ "UpstreamPathTemplate": "/",
+ "UpstreamHttpMethod": [
+ "Get"
+ ],
+ "AddHeadersToRequest": {},
+ "AddClaimsToRequest": {},
+ "RouteClaimsRequirement": {},
+ "AddQueriesToRequest": {},
+ "RequestIdKey": "",
+ "FileCacheOptions": {
+ "TtlSeconds": 0,
+ "Region": ""
+ },
+ "ReRouteIsCaseSensitive": false,
+ "ServiceName": "",
+ "DownstreamScheme": "http",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "localhost",
+ "Port": 51876,
+ }
+ ],
+ "QoSOptions": {
+ "ExceptionsAllowedBeforeBreaking": 0,
+ "DurationOfBreak": 0,
+ "TimeoutValue": 0
+ },
+ "LoadBalancer": "",
+ "RateLimitOptions": {
+ "ClientWhitelist": [],
+ "EnableRateLimiting": false,
+ "Period": "",
+ "PeriodTimespan": 0,
+ "Limit": 0
+ },
+ "AuthenticationOptions": {
+ "AuthenticationProviderKey": "",
+ "AllowedScopes": []
+ },
+ "HttpHandlerOptions": {
+ "AllowAutoRedirect": true,
+ "UseCookieContainer": true,
+ "UseTracing": true,
+ "MaxConnectionsPerServer": 100
+ },
+ "DangerousAcceptAnyServerCertificateValidator": false
+ }
+
+More information on how to use these options is below..
+
+Multiple environments
+^^^^^^^^^^^^^^^^^^^^^
+
+Like any other asp.net core project Ocelot supports configuration file names such as configuration.dev.json, configuration.test.json etc. In order to implement this add the following
+to you
+
+.. code-block:: csharp
+
+ .ConfigureAppConfiguration((hostingContext, config) =>
+ {
+ config
+ .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
+ .AddJsonFile("appsettings.json", true, true)
+ .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
+ .AddJsonFile("ocelot.json")
+ .AddJsonFile($"configuration.{hostingContext.HostingEnvironment.EnvironmentName}.json")
+ .AddEnvironmentVariables();
+ })
+
+Ocelot will now use the environment specific configuration and fall back to ocelot.json if there isn't one.
+
+You also need to set the corresponding environment variable which is ASPNETCORE_ENVIRONMENT. More info on this can be found in the `asp.net core docs `_.
+
+Merging configuration files
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This feature was requested in `Issue 296 `_ and allows users to have multiple configuration files to make managing large configurations easier.
+
+Instead of adding the configuration directly e.g. AddJsonFile("ocelot.json") you can call AddOcelot() like below.
+
+.. code-block:: csharp
+
+ .ConfigureAppConfiguration((hostingContext, config) =>
+ {
+ config
+ .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
+ .AddJsonFile("appsettings.json", true, true)
+ .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
+ .AddOcelot(hostingContext.HostingEnvironment)
+ .AddEnvironmentVariables();
+ })
+
+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.
+
+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.
+
+You can also give Ocelot a specific path to look in for the configuration files like below.
+
+.. code-block:: csharp
+
+ .ConfigureAppConfiguration((hostingContext, config) =>
+ {
+ config
+ .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
+ .AddJsonFile("appsettings.json", true, true)
+ .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
+ .AddOcelot("/foo/bar", hostingContext.HostingEnvironment)
+ .AddEnvironmentVariables();
+ })
+
+Ocelot needs the HostingEnvironment so it knows to exclude anything environment specific from the algorithm.
+
+Store configuration in 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 you add the following when you register your services Ocelot will attempt to store and retrieve its configuration in consul KV store.
+
+.. code-block:: csharp
+
+ services
+ .AddOcelot()
+ .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.
+
+.. code-block:: json
+
+ "GlobalConfiguration": {
+ "ServiceDiscoveryProvider": {
+ "Host": "localhost",
+ "Port": 9500
+ }
+ }
+
+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.
+
+Reload JSON config on change
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Ocelot supports reloading the json configuration file on change. e.g. the following will recreate Ocelots internal configuration when the ocelot.json file is updated
+manually.
+
+.. code-block:: json
+
+ config.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);
+
+Configuration Key
+-----------------
+
+If you are using Consul for configuration (or other providers in the future) you might want to key your configurations so you can have multiple configurations :) This feature was requested in `issue 346 `_! In order to specify the key you need to set the ConfigurationKey property in the ServiceDiscoveryProvider section of the configuration json file e.g.
+
+.. code-block:: json
+
+ "GlobalConfiguration": {
+ "ServiceDiscoveryProvider": {
+ "Host": "localhost",
+ "Port": 9500,
+ "ConfigurationKey": "Oceolot_A"
+ }
+ }
+
+In this example Ocelot will use Oceolot_A as the key for your configuration when looking it up in Consul.
+
+If you do not set the ConfigurationKey Ocelot will use the string InternalConfiguration as the key.
+
+Follow Redirects / Use CookieContainer
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Use HttpHandlerOptions in ReRoute configuration to set up HttpHandler behavior:
+
+1. AllowAutoRedirect is a value that indicates whether the request should follow redirection responses. Set it true if the request should automatically
+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!
+
+SSL Errors
+^^^^^^^^^^
+
+If you want to ignore SSL warnings / errors set the following in your ReRoute config.
+
+.. code-block:: json
+
+ "DangerousAcceptAnyServerCertificateValidator": true
+
+I don't recommend doing this, I suggest creating your own certificate and then getting it trusted by your local / remote machine if you can.
+
+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
diff --git a/docs/features/graphql.rst b/docs/features/graphql.rst
index 1b527314..36006fae 100644
--- a/docs/features/graphql.rst
+++ b/docs/features/graphql.rst
@@ -1,15 +1,15 @@
-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.
-
-
-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 :>
-
-
+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.
+
+
+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/servicediscovery.rst b/docs/features/servicediscovery.rst
index f36f9f78..f23d2a4a 100644
--- a/docs/features/servicediscovery.rst
+++ b/docs/features/servicediscovery.rst
@@ -155,6 +155,8 @@ Eureka. One of the services polls Eureka every 30 seconds (default) and gets the
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
^^^^^^^^^^^^^^^
diff --git a/docs/introduction/gettingstarted.rst b/docs/introduction/gettingstarted.rst
index cf2239fc..e2178c98 100644
--- a/docs/introduction/gettingstarted.rst
+++ b/docs/introduction/gettingstarted.rst
@@ -4,7 +4,7 @@ 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.
-.NET Core 2.1
+.NET Core 3.1
^^^^^^^^^^^^^
**Install NuGet package**
@@ -29,6 +29,30 @@ The following is a very basic ocelot.json. It won't do anything but should get O
}
}
+If you want some example that actually does something use the following:
+
+.. code-block:: json
+
+ {
+ "ReRoutes": [
+ {
+ "DownstreamPathTemplate": "/todos/{id}",
+ "DownstreamScheme": "https",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "jsonplaceholder.typicode.com",
+ "Port": 443
+ }
+ ],
+ "UpstreamPathTemplate": "/todos/{id}",
+ "UpstreamHttpMethod": [ "Get" ]
+ }
+ ],
+ "GlobalConfiguration": {
+ "BaseUrl": "https://localhost:5000"
+ }
+ }
+
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.
@@ -42,14 +66,20 @@ AddOcelot() (adds ocelot services), UseOcelot().Wait() (sets up all the Ocelot m
.. code-block:: csharp
+ using System.IO;
+ using Microsoft.AspNetCore.Hosting;
+ using Microsoft.Extensions.Configuration;
+ using Microsoft.Extensions.Hosting;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
-
- public class Program
+
+ namespace OcelotBasic
{
- public static void Main(string[] args)
+ public class Program
{
- new WebHostBuilder()
+ public static void Main(string[] args)
+ {
+ new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
@@ -74,9 +104,11 @@ AddOcelot() (adds ocelot services), UseOcelot().Wait() (sets up all the Ocelot m
app.UseOcelot().Wait();
})
.Build()
- .Run();
+ .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.
diff --git a/release.ps1 b/release.ps1
deleted file mode 100644
index c396b570..00000000
--- a/release.ps1
+++ /dev/null
@@ -1,2 +0,0 @@
-./build.ps1 -target Release
-exit $LASTEXITCODE
\ No newline at end of file
diff --git a/run-acceptance-tests.ps1 b/run-acceptance-tests.ps1
deleted file mode 100644
index 8f6b2dc2..00000000
--- a/run-acceptance-tests.ps1
+++ /dev/null
@@ -1,2 +0,0 @@
-./build -target RunAcceptanceTests
-exit $LASTEXITCODE
\ No newline at end of file
diff --git a/run-acceptance-tests.sh b/run-acceptance-tests.sh
deleted file mode 100755
index e05baea1..00000000
--- a/run-acceptance-tests.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env bash
-
-./build.sh --target RunAcceptanceTests
\ No newline at end of file
diff --git a/run-benchmarks.ps1 b/run-benchmarks.ps1
deleted file mode 100644
index 790ce6c6..00000000
--- a/run-benchmarks.ps1
+++ /dev/null
@@ -1,2 +0,0 @@
-./build.ps1 -target RunBenchmarkTests
-exit $LASTEXITCODE
\ No newline at end of file
diff --git a/run-unit-tests.ps1 b/run-unit-tests.ps1
deleted file mode 100644
index 444f0c46..00000000
--- a/run-unit-tests.ps1
+++ /dev/null
@@ -1,2 +0,0 @@
-./build.ps1 -target RunUnitTests
-exit $LASTEXITCODE
\ No newline at end of file
diff --git a/run-unit-tests.sh b/run-unit-tests.sh
deleted file mode 100755
index da848514..00000000
--- a/run-unit-tests.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env bash
-
-./build.sh --target RunUnitTests
\ No newline at end of file
diff --git a/samples/AdministrationApi/AdministrationApi.csproj b/samples/AdministrationApi/AdministrationApi.csproj
index bb8cf07f..3fcf5dc8 100644
--- a/samples/AdministrationApi/AdministrationApi.csproj
+++ b/samples/AdministrationApi/AdministrationApi.csproj
@@ -1,13 +1,13 @@
-
-
- netcoreapp2.1
-
-
-
-
-
-
-
-
-
+
+
+ netcoreapp3.1
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docker-compose.yaml b/samples/Docker-Compose/docker-compose.yaml
similarity index 95%
rename from docker-compose.yaml
rename to samples/Docker-Compose/docker-compose.yaml
index 83136844..5236202f 100644
--- a/docker-compose.yaml
+++ b/samples/Docker-Compose/docker-compose.yaml
@@ -1,24 +1,24 @@
-version: "3.4"
-services:
-
- tests:
- build:
- context: .
- target: builder
- volumes:
- - type: bind
- source: .
- target: /results
- command: test --logger:trx -r /results
-
- benchmarks:
- build:
- context: .
- target: builder
- args:
- build_configuration: Release
- command: run -c Release --project test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj 0 1 2 3 4
-
- manual-test:
- build: .
- ports: [ "5000:80" ]
+version: "3.4"
+services:
+
+ tests:
+ build:
+ context: .
+ target: builder
+ volumes:
+ - type: bind
+ source: .
+ target: /results
+ command: test --logger:trx -r /results
+
+ benchmarks:
+ build:
+ context: .
+ target: builder
+ args:
+ build_configuration: Release
+ command: run -c Release --project test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj 0 1 2 3 4
+
+ manual-test:
+ build: .
+ ports: [ "5000:80" ]
diff --git a/Dockerfile b/samples/Docker/Dockerfile
similarity index 98%
rename from Dockerfile
rename to samples/Docker/Dockerfile
index ebbd1eab..86b12ab0 100644
--- a/Dockerfile
+++ b/samples/Docker/Dockerfile
@@ -1,48 +1,48 @@
-#This is the base image used for any ran images
-FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-stretch-slim AS base
-WORKDIR /app
-EXPOSE 80
-
-#This image is used to build the source for the runnable app
-#It can also be used to run other CLI commands on the project, such as packing/deploying nuget packages. Some examples:
-#Run tests: docker build --target builder -t ocelot-build . && docker run ocelot-build test --logger:trx;LogFileName=results.trx
-#Run benchmarks: docker build --target builder --build-arg build_configuration=Release -t ocelot-build . && docker run ocelot-build run -c Release --project test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj
-FROM mcr.microsoft.com/dotnet/core/sdk:2.2-stretch AS builder
-WORKDIR /build
-#First we add only the project files so that we can cache nuget packages with dotnet restore
-COPY Ocelot.sln Ocelot.sln
-COPY src/Ocelot/Ocelot.csproj src/Ocelot/Ocelot.csproj
-COPY src/Ocelot.Administration/Ocelot.Administration.csproj src/Ocelot.Administration/Ocelot.Administration.csproj
-COPY src/Ocelot.Cache.CacheManager/Ocelot.Cache.CacheManager.csproj src/Ocelot.Cache.CacheManager/Ocelot.Cache.CacheManager.csproj
-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
-COPY test/Ocelot.ManualTest/Ocelot.ManualTest.csproj test/Ocelot.ManualTest/Ocelot.ManualTest.csproj
-COPY test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj
-COPY test/Ocelot.UnitTests/Ocelot.UnitTests.csproj test/Ocelot.UnitTests/Ocelot.UnitTests.csproj
-COPY test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj
-
-RUN dotnet restore
-#Now we add the rest of the source and run a complete build... --no-restore is used because nuget should be resolved at this point
-COPY codeanalysis.ruleset codeanalysis.ruleset
-COPY src src
-COPY test test
-ARG build_configuration=Debug
-RUN dotnet build --no-restore -c ${build_configuration}
-ENTRYPOINT ["dotnet"]
-
-#This is just for holding the published manual tests...
-FROM builder AS manual-test-publish
-ARG build_configuration=Debug
-RUN dotnet publish --no-build -c ${build_configuration} -o /app test/Ocelot.ManualTest
-
-#Run manual tests! This is the default run option.
-#docker build -t ocelot-manual-test . && docker run --net host ocelot-manual-test
-FROM base AS manual-test
-ENV ASPNETCORE_ENVIRONMENT=Development
-COPY --from=manual-test-publish /app .
-ENTRYPOINT ["dotnet", "Ocelot.ManualTest.dll"]
+#This is the base image used for any ran images
+FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-stretch-slim AS base
+WORKDIR /app
+EXPOSE 80
+
+#This image is used to build the source for the runnable app
+#It can also be used to run other CLI commands on the project, such as packing/deploying nuget packages. Some examples:
+#Run tests: docker build --target builder -t ocelot-build . && docker run ocelot-build test --logger:trx;LogFileName=results.trx
+#Run benchmarks: docker build --target builder --build-arg build_configuration=Release -t ocelot-build . && docker run ocelot-build run -c Release --project test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj
+FROM mcr.microsoft.com/dotnet/core/sdk:2.2-stretch AS builder
+WORKDIR /build
+#First we add only the project files so that we can cache nuget packages with dotnet restore
+COPY Ocelot.sln Ocelot.sln
+COPY src/Ocelot/Ocelot.csproj src/Ocelot/Ocelot.csproj
+COPY src/Ocelot.Administration/Ocelot.Administration.csproj src/Ocelot.Administration/Ocelot.Administration.csproj
+COPY src/Ocelot.Cache.CacheManager/Ocelot.Cache.CacheManager.csproj src/Ocelot.Cache.CacheManager/Ocelot.Cache.CacheManager.csproj
+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
+COPY test/Ocelot.ManualTest/Ocelot.ManualTest.csproj test/Ocelot.ManualTest/Ocelot.ManualTest.csproj
+COPY test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj
+COPY test/Ocelot.UnitTests/Ocelot.UnitTests.csproj test/Ocelot.UnitTests/Ocelot.UnitTests.csproj
+COPY test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj
+
+RUN dotnet restore
+#Now we add the rest of the source and run a complete build... --no-restore is used because nuget should be resolved at this point
+COPY codeanalysis.ruleset codeanalysis.ruleset
+COPY src src
+COPY test test
+ARG build_configuration=Debug
+RUN dotnet build --no-restore -c ${build_configuration}
+ENTRYPOINT ["dotnet"]
+
+#This is just for holding the published manual tests...
+FROM builder AS manual-test-publish
+ARG build_configuration=Debug
+RUN dotnet publish --no-build -c ${build_configuration} -o /app test/Ocelot.ManualTest
+
+#Run manual tests! This is the default run option.
+#docker build -t ocelot-manual-test . && docker run --net host ocelot-manual-test
+FROM base AS manual-test
+ENV ASPNETCORE_ENVIRONMENT=Development
+COPY --from=manual-test-publish /app .
+ENTRYPOINT ["dotnet", "Ocelot.ManualTest.dll"]
diff --git a/samples/Docker/README.md b/samples/Docker/README.md
new file mode 100644
index 00000000..e69de29b
diff --git a/samples/OcelotBasic/OcelotBasic.csproj b/samples/OcelotBasic/OcelotBasic.csproj
new file mode 100644
index 00000000..5e290206
--- /dev/null
+++ b/samples/OcelotBasic/OcelotBasic.csproj
@@ -0,0 +1,16 @@
+
+
+
+ netcoreapp3.1
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/OcelotBasic/Program.cs b/samples/OcelotBasic/Program.cs
new file mode 100644
index 00000000..c0d8bed6
--- /dev/null
+++ b/samples/OcelotBasic/Program.cs
@@ -0,0 +1,42 @@
+using System.IO;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Hosting;
+using Ocelot.DependencyInjection;
+using Ocelot.Middleware;
+
+namespace OcelotBasic
+{
+ public class Program
+ {
+ 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();
+ }
+ }
+}
diff --git a/samples/OcelotBasic/Properties/launchSettings.json b/samples/OcelotBasic/Properties/launchSettings.json
new file mode 100644
index 00000000..b500ae57
--- /dev/null
+++ b/samples/OcelotBasic/Properties/launchSettings.json
@@ -0,0 +1,27 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:55029/",
+ "sslPort": 44390
+ }
+ },
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "OcelotBasic": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "https://localhost:5001;http://localhost:5000"
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/OcelotBasic/appsettings.Development.json b/samples/OcelotBasic/appsettings.Development.json
new file mode 100644
index 00000000..dba68eb1
--- /dev/null
+++ b/samples/OcelotBasic/appsettings.Development.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ }
+}
diff --git a/samples/OcelotBasic/appsettings.json b/samples/OcelotBasic/appsettings.json
new file mode 100644
index 00000000..81ff8777
--- /dev/null
+++ b/samples/OcelotBasic/appsettings.json
@@ -0,0 +1,10 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/samples/OcelotBasic/ocelot.json b/samples/OcelotBasic/ocelot.json
new file mode 100644
index 00000000..7a1759ff
--- /dev/null
+++ b/samples/OcelotBasic/ocelot.json
@@ -0,0 +1,21 @@
+{
+ "ReRoutes": [
+ {
+ "DownstreamPathTemplate": "/todos/{id}",
+ "DownstreamScheme": "https",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "jsonplaceholder.typicode.com",
+ "Port": 443
+ }
+ ],
+ "UpstreamPathTemplate": "/posts/{id}",
+ "UpstreamHttpMethod": [
+ "Get"
+ ]
+ }
+ ],
+ "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 a1fe22dd..a257c980 100644
--- a/samples/OcelotEureka/ApiGateway/ApiGateway.csproj
+++ b/samples/OcelotEureka/ApiGateway/ApiGateway.csproj
@@ -1,24 +1,23 @@
-
-
-
- netcoreapp2.2
-
-
-
-
-
-
-
-
- PreserveNewest
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ netcoreapp3.1
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/OcelotEureka/DownstreamService/DownstreamService.csproj b/samples/OcelotEureka/DownstreamService/DownstreamService.csproj
index 3d5689a5..209fa6d2 100644
--- a/samples/OcelotEureka/DownstreamService/DownstreamService.csproj
+++ b/samples/OcelotEureka/DownstreamService/DownstreamService.csproj
@@ -1,20 +1,19 @@
-
-
-
- netcoreapp2.2
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ netcoreapp3.1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/OcelotGraphQL/OcelotGraphQL.csproj b/samples/OcelotGraphQL/OcelotGraphQL.csproj
index 6577b719..408d8543 100644
--- a/samples/OcelotGraphQL/OcelotGraphQL.csproj
+++ b/samples/OcelotGraphQL/OcelotGraphQL.csproj
@@ -1,18 +1,17 @@
-
-
- netcoreapp2.0
-
-
-
- PreserveNewest
-
-
-
-
-
-
-
-
-
-
+
+
+ netcoreapp3.1
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/OcelotKube/ApiGateway/ApiGateway.csproj b/samples/OcelotKube/ApiGateway/ApiGateway.csproj
index 81b47303..05f462ab 100644
--- a/samples/OcelotKube/ApiGateway/ApiGateway.csproj
+++ b/samples/OcelotKube/ApiGateway/ApiGateway.csproj
@@ -1,17 +1,17 @@
-
-
-
- netcoreapp2.2
- InProcess
- Linux
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ netcoreapp3.1
+ InProcess
+ Linux
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/OcelotKube/DownstreamService/DownstreamService.csproj b/samples/OcelotKube/DownstreamService/DownstreamService.csproj
index 88fdbfb7..d430c524 100644
--- a/samples/OcelotKube/DownstreamService/DownstreamService.csproj
+++ b/samples/OcelotKube/DownstreamService/DownstreamService.csproj
@@ -1,15 +1,15 @@
-
-
-
- netcoreapp2.2
- InProcess
- Linux
-
-
-
-
-
-
-
-
-
+
+
+
+ netcoreapp3.1
+ InProcess
+ Linux
+
+
+
+
+
+
+
+
+
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.csproj b/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.csproj
index 08324cbe..186b43f3 100644
--- a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.csproj
+++ b/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.csproj
@@ -1,22 +1,22 @@
-
-
- Stateless Web Service for Stateful OcelotApplicationApiGateway App
-
- netcoreapp2.0
- OcelotApplicationApiGateway
- Exe
- OcelotApplicationApiGateway
-
-
-
- PreserveNewest
-
-
-
-
-
-
-
-
-
+
+
+ Stateless Web Service for Stateful OcelotApplicationApiGateway App
+
+ netcoreapp3.1
+ OcelotApplicationApiGateway
+ Exe
+ OcelotApplicationApiGateway
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationService/OcelotApplicationService.csproj b/samples/OcelotServiceFabric/src/OcelotApplicationService/OcelotApplicationService.csproj
index 34991440..ea45d737 100644
--- a/samples/OcelotServiceFabric/src/OcelotApplicationService/OcelotApplicationService.csproj
+++ b/samples/OcelotServiceFabric/src/OcelotApplicationService/OcelotApplicationService.csproj
@@ -1,21 +1,21 @@
-
-
- Stateless Service Application
-
- Exe
- netcoreapp2.0
- OcelotApplicationService
- OcelotApplicationService
- $(PackageTargetFallback)
-
-
-
-
-
-
-
-
-
-
-
+
+
+ Stateless Service Application
+
+ Exe
+ netcoreapp3.1
+ OcelotApplicationService
+ OcelotApplicationService
+ $(PackageTargetFallback)
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Ocelot.Administration/Ocelot.Administration.csproj b/src/Ocelot.Administration/Ocelot.Administration.csproj
index abf0f33f..c1be0599 100644
--- a/src/Ocelot.Administration/Ocelot.Administration.csproj
+++ b/src/Ocelot.Administration/Ocelot.Administration.csproj
@@ -1,39 +1,39 @@
-
-
- netcoreapp3.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://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
-
-
-
-
-
-
-
-
+
+
+ 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
+
+
+
+
+
+
+
+
diff --git a/src/Ocelot.Cache.CacheManager/Ocelot.Cache.CacheManager.csproj b/src/Ocelot.Cache.CacheManager/Ocelot.Cache.CacheManager.csproj
index dab96133..4c575fe5 100644
--- a/src/Ocelot.Cache.CacheManager/Ocelot.Cache.CacheManager.csproj
+++ b/src/Ocelot.Cache.CacheManager/Ocelot.Cache.CacheManager.csproj
@@ -1,39 +1,39 @@
-
-
- netcoreapp3.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://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
-
-
-
-
-
-
-
-
-
+
+
+ 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
+
+
+
+
+
+
+
+
+
diff --git a/src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj b/src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj
index 61df395b..ece85c9b 100644
--- a/src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj
+++ b/src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj
@@ -1,38 +1,38 @@
-
-
- netcoreapp3.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://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
-
-
-
-
-
-
+
+
+ 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
+
+
+
+
+
+
diff --git a/src/Ocelot.Provider.Eureka/Eureka.cs b/src/Ocelot.Provider.Eureka/Eureka.cs
index 8316beac..8a064549 100644
--- a/src/Ocelot.Provider.Eureka/Eureka.cs
+++ b/src/Ocelot.Provider.Eureka/Eureka.cs
@@ -26,7 +26,7 @@
if (instances != null && instances.Any())
{
- services.AddRange(instances.Select(i => new Service(i.ServiceId, new ServiceHostAndPort(i.Host, i.Port), "", "", new List())));
+ services.AddRange(instances.Select(i => new Service(i.ServiceId, new ServiceHostAndPort(i.Host, i.Port, i.Uri.Scheme), "", "", new List())));
}
return Task.FromResult(services);
diff --git a/src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj b/src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj
index 2254cc2a..587071e0 100644
--- a/src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj
+++ b/src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj
@@ -1,38 +1,38 @@
-
-
- netcoreapp3.0
- true
- Provides Ocelot extensions to use Eureka
- Ocelot.Provider.Eureka
- 0.0.0-dev
- Ocelot.Provider.Eureka
- Ocelot.Provider.Eureka
- 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
- win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64
- false
- false
- True
- false
- Tom Pallister
- ..\..\codeanalysis.ruleset
-
-
- full
- True
-
-
-
-
-
-
-
- all
-
-
-
-
-
-
+
+
+ netcoreapp3.1
+ true
+ Provides Ocelot extensions to use Eureka
+ Ocelot.Provider.Eureka
+ 0.0.0-dev
+ Ocelot.Provider.Eureka
+ Ocelot.Provider.Eureka
+ 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
+ win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64
+ false
+ false
+ True
+ false
+ Tom Pallister
+ ..\..\codeanalysis.ruleset
+
+
+ full
+ True
+
+
+
+
+
+
+
+ all
+
+
+
+
+
+
diff --git a/src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj b/src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj
index 6dba88d1..980cd909 100644
--- a/src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj
+++ b/src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj
@@ -1,43 +1,43 @@
-
-
-
- netcoreapp3.0
- true
- Ocelot
- Provides Ocelot extensions to use kubernetes
- https://github.com/ThreeMammals/Ocelot
- http://threemammals.com/images/ocelot_logo.png
-
- Ocelot.Provider.Kubernetes
- Ocelot.Provider.Kubernetes
- API Gateway;.NET core
- win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64
- false
- false
- true
- false
- 0.0.0-dev
- geffzhang
-
- ..\..\codeanalysis.ruleset
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ netcoreapp3.1
+ true
+ Ocelot
+ Provides Ocelot extensions to use kubernetes
+ https://github.com/ThreeMammals/Ocelot
+ http://threemammals.com/images/ocelot_logo.png
+
+ Ocelot.Provider.Kubernetes
+ Ocelot.Provider.Kubernetes
+ API Gateway;.NET core
+ win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64
+ false
+ false
+ true
+ false
+ 0.0.0-dev
+ geffzhang
+
+ ..\..\codeanalysis.ruleset
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj b/src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj
index 36143432..447db1da 100644
--- a/src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj
+++ b/src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj
@@ -1,38 +1,38 @@
-
-
- netcoreapp3.0
- true
- Provides Ocelot extensions to use Polly.NET
- Ocelot.Provider.Polly
- 0.0.0-dev
- Ocelot.Provider.Polly
- Ocelot.Provider.Polly
- API Gateway;.NET core
- https://github.com/ThreeMammals/Ocelot.Provider.Polly
- https://github.com/ThreeMammals/Ocelot.Provider.Polly
- 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
-
-
-
-
-
-
-
+
+
+ netcoreapp3.1
+ true
+ Provides Ocelot extensions to use Polly.NET
+ Ocelot.Provider.Polly
+ 0.0.0-dev
+ Ocelot.Provider.Polly
+ Ocelot.Provider.Polly
+ API Gateway;.NET core
+ https://github.com/ThreeMammals/Ocelot.Provider.Polly
+ https://github.com/ThreeMammals/Ocelot.Provider.Polly
+ 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
+
+
+
+
+
+
+
diff --git a/src/Ocelot.Provider.Rafty/Ocelot.Provider.Rafty.csproj b/src/Ocelot.Provider.Rafty/Ocelot.Provider.Rafty.csproj
index e6699562..8cb2ab42 100644
--- a/src/Ocelot.Provider.Rafty/Ocelot.Provider.Rafty.csproj
+++ b/src/Ocelot.Provider.Rafty/Ocelot.Provider.Rafty.csproj
@@ -1,40 +1,40 @@
-
-
- netcoreapp3.0
- true
- Provides Ocelot extensions to use Rafty
- Ocelot.Provider.Rafty
- 0.0.0-dev
- Ocelot.Provider.Rafty
- Ocelot.Provider.Rafty
- API Gateway;.NET core
- https://github.com/ThreeMammals/Ocelot.Provider.Rafty
- https://github.com/ThreeMammals/Ocelot.Provider.Rafty
- 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
-
-
-
-
-
-
+
+
+ netcoreapp3.1
+ true
+ Provides Ocelot extensions to use Rafty
+ Ocelot.Provider.Rafty
+ 0.0.0-dev
+ Ocelot.Provider.Rafty
+ Ocelot.Provider.Rafty
+ API Gateway;.NET core
+ https://github.com/ThreeMammals/Ocelot.Provider.Rafty
+ https://github.com/ThreeMammals/Ocelot.Provider.Rafty
+ 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
+
+
+
+
+
+
diff --git a/src/Ocelot.Tracing.Butterfly/Ocelot.Tracing.Butterfly.csproj b/src/Ocelot.Tracing.Butterfly/Ocelot.Tracing.Butterfly.csproj
index 6f2e8141..f2401e02 100644
--- a/src/Ocelot.Tracing.Butterfly/Ocelot.Tracing.Butterfly.csproj
+++ b/src/Ocelot.Tracing.Butterfly/Ocelot.Tracing.Butterfly.csproj
@@ -1,38 +1,38 @@
-
-
-
- netcoreapp3.0
- true
- This package provides methods to integrate Butterfly tracing with Ocelot.
- Ocelot.Tracing.Butterfly
- 0.0.0-dev
- Ocelot.Tracing.Butterfly
- Ocelot.Tracing.Butterfly
- API Gateway;.NET core; Butterfly; ButterflyAPM
- https://github.com/ThreeMammals/Ocelot
- https://github.com/ThreeMammals/Ocelot
- win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64
- false
- false
- True
- false
- Tom Pallister
- ..\..\codeanalysis.ruleset
- Ocelot.Tracing.Butterfly
-
-
- full
- True
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ netcoreapp3.1
+ true
+ This package provides methods to integrate Butterfly tracing with Ocelot.
+ Ocelot.Tracing.Butterfly
+ 0.0.0-dev
+ Ocelot.Tracing.Butterfly
+ Ocelot.Tracing.Butterfly
+ API Gateway;.NET core; Butterfly; ButterflyAPM
+ https://github.com/ThreeMammals/Ocelot
+ https://github.com/ThreeMammals/Ocelot
+ win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64
+ false
+ false
+ True
+ false
+ Tom Pallister
+ ..\..\codeanalysis.ruleset
+ Ocelot.Tracing.Butterfly
+
+
+ full
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Ocelot/Configuration/Builder/RateLimitOptionsBuilder.cs b/src/Ocelot/Configuration/Builder/RateLimitOptionsBuilder.cs
index eba79769..eaaa4dab 100644
--- a/src/Ocelot/Configuration/Builder/RateLimitOptionsBuilder.cs
+++ b/src/Ocelot/Configuration/Builder/RateLimitOptionsBuilder.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
namespace Ocelot.Configuration.Builder
{
@@ -7,6 +8,7 @@ namespace Ocelot.Configuration.Builder
private bool _enableRateLimiting;
private string _clientIdHeader;
private List _clientWhitelist;
+ private Func> _getClientWhitelist;
private bool _disableRateLimitHeaders;
private string _quotaExceededMessage;
private string _rateLimitCounterPrefix;
@@ -19,15 +21,15 @@ namespace Ocelot.Configuration.Builder
return this;
}
- public RateLimitOptionsBuilder WithClientIdHeader(string clientIdheader)
+ public RateLimitOptionsBuilder WithClientIdHeader(string clientIdHeader)
{
- _clientIdHeader = clientIdheader;
+ _clientIdHeader = clientIdHeader;
return this;
}
- public RateLimitOptionsBuilder WithClientWhiteList(List clientWhitelist)
+ public RateLimitOptionsBuilder WithClientWhiteList(Func> getClientWhitelist)
{
- _clientWhitelist = clientWhitelist;
+ _getClientWhitelist = getClientWhitelist;
return this;
}
@@ -63,9 +65,9 @@ namespace Ocelot.Configuration.Builder
public RateLimitOptions Build()
{
- return new RateLimitOptions(_enableRateLimiting, _clientIdHeader, _clientWhitelist,
- _disableRateLimitHeaders, _quotaExceededMessage, _rateLimitCounterPrefix,
+ return new RateLimitOptions(_enableRateLimiting, _clientIdHeader, _getClientWhitelist,
+ _disableRateLimitHeaders, _quotaExceededMessage, _rateLimitCounterPrefix,
_rateLimitRule, _httpStatusCode);
}
}
-}
+}
diff --git a/src/Ocelot/Configuration/Creator/HttpHandlerOptionsCreator.cs b/src/Ocelot/Configuration/Creator/HttpHandlerOptionsCreator.cs
index 08e79ad1..4e38100b 100644
--- a/src/Ocelot/Configuration/Creator/HttpHandlerOptionsCreator.cs
+++ b/src/Ocelot/Configuration/Creator/HttpHandlerOptionsCreator.cs
@@ -18,8 +18,11 @@
{
var useTracing = _tracer != null && options.UseTracing;
+ //be sure that maxConnectionPerServer is in correct range of values
+ int maxConnectionPerServer = (options.MaxConnectionsPerServer > 0) ? maxConnectionPerServer = options.MaxConnectionsPerServer : maxConnectionPerServer = int.MaxValue;
+
return new HttpHandlerOptions(options.AllowAutoRedirect,
- options.UseCookieContainer, useTracing, options.UseProxy);
+ options.UseCookieContainer, useTracing, options.UseProxy, maxConnectionPerServer);
}
}
}
diff --git a/src/Ocelot/Configuration/Creator/RateLimitOptionsCreator.cs b/src/Ocelot/Configuration/Creator/RateLimitOptionsCreator.cs
index 8f300dd2..ba167bfe 100644
--- a/src/Ocelot/Configuration/Creator/RateLimitOptionsCreator.cs
+++ b/src/Ocelot/Configuration/Creator/RateLimitOptionsCreator.cs
@@ -11,7 +11,7 @@ namespace Ocelot.Configuration.Creator
{
return new RateLimitOptionsBuilder()
.WithClientIdHeader(globalConfiguration.RateLimitOptions.ClientIdHeader)
- .WithClientWhiteList(fileRateLimitRule.ClientWhitelist)
+ .WithClientWhiteList(() => fileRateLimitRule.ClientWhitelist)
.WithDisableRateLimitHeaders(globalConfiguration.RateLimitOptions.DisableRateLimitHeaders)
.WithEnableRateLimiting(fileRateLimitRule.EnableRateLimiting)
.WithHttpStatusCode(globalConfiguration.RateLimitOptions.HttpStatusCode)
diff --git a/src/Ocelot/Configuration/File/FileHttpHandlerOptions.cs b/src/Ocelot/Configuration/File/FileHttpHandlerOptions.cs
index d765af58..b83be428 100644
--- a/src/Ocelot/Configuration/File/FileHttpHandlerOptions.cs
+++ b/src/Ocelot/Configuration/File/FileHttpHandlerOptions.cs
@@ -6,7 +6,8 @@
{
AllowAutoRedirect = false;
UseCookieContainer = false;
- UseProxy = true;
+ UseProxy = true;
+ MaxConnectionsPerServer = int.MaxValue;
}
public bool AllowAutoRedirect { get; set; }
@@ -15,6 +16,8 @@
public bool UseTracing { get; set; }
- public bool UseProxy { get; set; }
+ public bool UseProxy { get; set; }
+
+ public int MaxConnectionsPerServer { get; set; }
}
}
diff --git a/src/Ocelot/Configuration/HttpHandlerOptions.cs b/src/Ocelot/Configuration/HttpHandlerOptions.cs
index b551287f..e76cc117 100644
--- a/src/Ocelot/Configuration/HttpHandlerOptions.cs
+++ b/src/Ocelot/Configuration/HttpHandlerOptions.cs
@@ -6,32 +6,44 @@
///
public class HttpHandlerOptions
{
- public HttpHandlerOptions(bool allowAutoRedirect, bool useCookieContainer, bool useTracing, bool useProxy)
- {
- AllowAutoRedirect = allowAutoRedirect;
- UseCookieContainer = useCookieContainer;
- UseTracing = useTracing;
- UseProxy = useProxy;
+ public HttpHandlerOptions(bool allowAutoRedirect, bool useCookieContainer, bool useTracing, bool useProxy, int maxConnectionsPerServer)
+ {
+ AllowAutoRedirect = allowAutoRedirect;
+ UseCookieContainer = useCookieContainer;
+ UseTracing = useTracing;
+ UseProxy = useProxy;
+ MaxConnectionsPerServer = maxConnectionsPerServer;
}
+
///
/// Specify if auto redirect is enabled
- ///
- public bool AllowAutoRedirect { get; private set; }
-
+ ///
+ /// AllowAutoRedirect
+ public bool AllowAutoRedirect { get; private set; }
+
///
/// Specify is handler has to use a cookie container
- ///
+ ///
+ /// UseCookieContainer
public bool UseCookieContainer { get; private set; }
///
/// Specify is handler has to use a opentracing
- ///
+ ///
+ /// UseTracing
public bool UseTracing { get; private set; }
///
/// Specify if handler has to use a proxy
- ///
- public bool UseProxy { get; private set; }
+ ///
+ /// UseProxy
+ public bool UseProxy { get; private set; }
+
+ ///
+ /// Specify the maximum of concurrent connection to a network endpoint
+ ///
+ /// MaxConnectionsPerServer
+ public int MaxConnectionsPerServer { get; private set; }
}
}
diff --git a/src/Ocelot/Configuration/HttpHandlerOptionsBuilder.cs b/src/Ocelot/Configuration/HttpHandlerOptionsBuilder.cs
index bf8aa042..8e66094b 100644
--- a/src/Ocelot/Configuration/HttpHandlerOptionsBuilder.cs
+++ b/src/Ocelot/Configuration/HttpHandlerOptionsBuilder.cs
@@ -6,6 +6,7 @@
private bool _useCookieContainer;
private bool _useTracing;
private bool _useProxy;
+ private int _maxConnectionPerServer;
public HttpHandlerOptionsBuilder WithAllowAutoRedirect(bool input)
{
@@ -30,10 +31,16 @@
_useProxy = useProxy;
return this;
}
+ public HttpHandlerOptionsBuilder WithUseMaxConnectionPerServer(int maxConnectionPerServer)
+ {
+ _maxConnectionPerServer = maxConnectionPerServer;
+ return this;
+ }
+
public HttpHandlerOptions Build()
{
- return new HttpHandlerOptions(_allowAutoRedirect, _useCookieContainer, _useTracing, _useProxy);
+ return new HttpHandlerOptions(_allowAutoRedirect, _useCookieContainer, _useTracing, _useProxy, _maxConnectionPerServer);
}
}
}
diff --git a/src/Ocelot/Configuration/RateLimitOptions.cs b/src/Ocelot/Configuration/RateLimitOptions.cs
index db1da8eb..28472825 100644
--- a/src/Ocelot/Configuration/RateLimitOptions.cs
+++ b/src/Ocelot/Configuration/RateLimitOptions.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
namespace Ocelot.Configuration
{
@@ -7,12 +8,14 @@ namespace Ocelot.Configuration
///
public class RateLimitOptions
{
- public RateLimitOptions(bool enbleRateLimiting, string clientIdHeader, List clientWhitelist, bool disableRateLimitHeaders,
+ private readonly Func> _getClientWhitelist;
+
+ public RateLimitOptions(bool enableRateLimiting, string clientIdHeader, Func> getClientWhitelist, bool disableRateLimitHeaders,
string quotaExceededMessage, string rateLimitCounterPrefix, RateLimitRule rateLimitRule, int httpStatusCode)
{
- EnableRateLimiting = enbleRateLimiting;
+ EnableRateLimiting = enableRateLimiting;
ClientIdHeader = clientIdHeader;
- ClientWhitelist = clientWhitelist ?? new List();
+ _getClientWhitelist = getClientWhitelist;
DisableRateLimitHeaders = disableRateLimitHeaders;
QuotaExceededMessage = quotaExceededMessage;
RateLimitCounterPrefix = rateLimitCounterPrefix;
@@ -22,18 +25,21 @@ namespace Ocelot.Configuration
public RateLimitRule RateLimitRule { get; private set; }
- public List ClientWhitelist { get; private set; }
+ ///
+ /// Gets the list of white listed clients
+ ///
+ public List ClientWhitelist { get => _getClientWhitelist(); }
///
/// Gets or sets the HTTP header that holds the client identifier, by default is X-ClientId
///
- public string ClientIdHeader { get; private set; }
-
+ public string ClientIdHeader { get; private set; }
+
///
/// Gets or sets the HTTP Status code returned when rate limiting occurs, by default value is set to 429 (Too Many Requests)
///
- public int HttpStatusCode { get; private set; }
-
+ public int HttpStatusCode { get; private set; }
+
///
/// Gets or sets a value that will be used as a formatter for the QuotaExceeded response message.
/// If none specified the default will be:
@@ -44,8 +50,8 @@ namespace Ocelot.Configuration
///
/// Gets or sets the counter prefix, used to compose the rate limit counter cache key
///
- public string RateLimitCounterPrefix { get; private set; }
-
+ public string RateLimitCounterPrefix { get; private set; }
+
///
/// Enables endpoint rate limiting based URL path and HTTP verb
///
@@ -56,4 +62,4 @@ namespace Ocelot.Configuration
///
public bool DisableRateLimitHeaders { get; private set; }
}
-}
+}
diff --git a/src/Ocelot/Configuration/Repository/FileConfigurationPoller.cs b/src/Ocelot/Configuration/Repository/FileConfigurationPoller.cs
index cca50d0b..9a2b67e6 100644
--- a/src/Ocelot/Configuration/Repository/FileConfigurationPoller.cs
+++ b/src/Ocelot/Configuration/Repository/FileConfigurationPoller.cs
@@ -105,7 +105,8 @@ namespace Ocelot.Configuration.Repository
public void Dispose()
{
- _timer.Dispose();
+ _timer?.Dispose();
+ _timer = null;
}
}
}
diff --git a/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs b/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs
index add4fe0b..e2f7f24d 100644
--- a/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs
+++ b/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs
@@ -9,6 +9,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+ using System.Text.RegularExpressions;
using System.Threading.Tasks;
public class FileConfigurationFluentValidator : AbstractValidator, IConfigurationValidator
@@ -35,6 +36,10 @@
.Must((config, reRoute) => HaveServiceDiscoveryProviderRegistered(reRoute, config.GlobalConfiguration.ServiceDiscoveryProvider))
.WithMessage((config, reRoute) => $"Unable to start Ocelot, errors are: Unable to start Ocelot because either a ReRoute or GlobalConfiguration are using ServiceDiscoveryOptions but no ServiceDiscoveryFinderDelegate has been registered in dependency injection container. Are you missing a package like Ocelot.Provider.Consul and services.AddConsul() or Ocelot.Provider.Eureka and services.AddEureka()?");
+ RuleForEach(configuration => configuration.ReRoutes)
+ .Must((config, reRoute) => IsPlaceholderNotDuplicatedIn(reRoute.UpstreamPathTemplate))
+ .WithMessage((config, reRoute) => $"{nameof(reRoute)} {reRoute.UpstreamPathTemplate} has duplicated placeholder");
+
RuleFor(configuration => configuration.GlobalConfiguration.ServiceDiscoveryProvider)
.Must(HaveServiceDiscoveryProviderRegistered)
.WithMessage((config, reRoute) => $"Unable to start Ocelot, errors are: Unable to start Ocelot because either a ReRoute or GlobalConfiguration are using ServiceDiscoveryOptions but no ServiceDiscoveryFinderDelegate has been registered in dependency injection container. Are you missing a package like Ocelot.Provider.Consul and services.AddConsul() or Ocelot.Provider.Eureka and services.AddEureka()?");
@@ -109,6 +114,14 @@
return reRoutesForAggregate.Count() == fileAggregateReRoute.ReRouteKeys.Count;
}
+ private bool IsPlaceholderNotDuplicatedIn(string upstreamPathTemplate)
+ {
+ Regex regExPlaceholder = new Regex("{[^}]+}");
+ var matches = regExPlaceholder.Matches(upstreamPathTemplate);
+ var upstreamPathPlaceholders = matches.Select(m => m.Value);
+ return upstreamPathPlaceholders.Count() == upstreamPathPlaceholders.Distinct().Count();
+ }
+
private static bool DoesNotContainReRoutesWithSpecificRequestIdKeys(FileAggregateReRoute fileAggregateReRoute,
List reRoutes)
{
diff --git a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs
index d2e53b95..7d19d523 100644
--- a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs
+++ b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs
@@ -37,7 +37,10 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
return;
}
- context.DownstreamRequest.Scheme = context.DownstreamReRoute.DownstreamScheme;
+ if (!string.IsNullOrEmpty(context.DownstreamReRoute.DownstreamScheme))
+ {
+ context.DownstreamRequest.Scheme = context.DownstreamReRoute.DownstreamScheme;
+ }
if (ServiceFabricRequest(context))
{
diff --git a/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs b/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs
index 22456430..646af5fe 100644
--- a/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs
+++ b/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs
@@ -45,6 +45,11 @@ namespace Ocelot.LoadBalancer.Middleware
context.DownstreamRequest.Port = hostAndPort.Data.DownstreamPort;
}
+ if (!string.IsNullOrEmpty(hostAndPort.Data.Scheme))
+ {
+ context.DownstreamRequest.Scheme = hostAndPort.Data.Scheme;
+ }
+
try
{
await _next.Invoke(context);
diff --git a/src/Ocelot/Ocelot.csproj b/src/Ocelot/Ocelot.csproj
index fb0d541a..7c290d0a 100644
--- a/src/Ocelot/Ocelot.csproj
+++ b/src/Ocelot/Ocelot.csproj
@@ -1,42 +1,42 @@
-
-
- netcoreapp3.0
- true
- Ocelot is an API Gateway. The project 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. reference 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 retrived 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
- 0.0.0-dev
- Ocelot
- Ocelot
- API Gateway;.NET core
- https://github.com/ThreeMammals/Ocelot
- https://github.com/ThreeMammals/Ocelot
- 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
-
-
-
-
-
-
- NU1701
-
-
-
- all
-
-
-
-
-
-
-
+
+
+ netcoreapp3.1
+ true
+ Ocelot is an API Gateway. The project 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. reference 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 retrived 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
+ 0.0.0-dev
+ Ocelot
+ Ocelot
+ API Gateway;.NET core
+ https://github.com/ThreeMammals/Ocelot
+ https://github.com/ThreeMammals/Ocelot
+ 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
+
+
+
+
+
+
+ NU1701
+
+
+
+ all
+
+
+
+
+
+
+
diff --git a/src/Ocelot/Requester/HttpClientBuilder.cs b/src/Ocelot/Requester/HttpClientBuilder.cs
index 64df44b1..e7d45857 100644
--- a/src/Ocelot/Requester/HttpClientBuilder.cs
+++ b/src/Ocelot/Requester/HttpClientBuilder.cs
@@ -90,7 +90,9 @@ namespace Ocelot.Requester
{
AllowAutoRedirect = context.DownstreamReRoute.HttpHandlerOptions.AllowAutoRedirect,
UseCookies = context.DownstreamReRoute.HttpHandlerOptions.UseCookieContainer,
- UseProxy = context.DownstreamReRoute.HttpHandlerOptions.UseProxy
+ UseProxy = context.DownstreamReRoute.HttpHandlerOptions.UseProxy,
+ MaxConnectionsPerServer = context.DownstreamReRoute.HttpHandlerOptions.MaxConnectionsPerServer
+
};
}
@@ -101,6 +103,7 @@ namespace Ocelot.Requester
AllowAutoRedirect = context.DownstreamReRoute.HttpHandlerOptions.AllowAutoRedirect,
UseCookies = context.DownstreamReRoute.HttpHandlerOptions.UseCookieContainer,
UseProxy = context.DownstreamReRoute.HttpHandlerOptions.UseProxy,
+ MaxConnectionsPerServer = context.DownstreamReRoute.HttpHandlerOptions.MaxConnectionsPerServer,
CookieContainer = new CookieContainer()
};
}
diff --git a/src/Ocelot/Requester/HttpExeptionToErrorMapper.cs b/src/Ocelot/Requester/HttpExeptionToErrorMapper.cs
index 80cec0ea..f696a02b 100644
--- a/src/Ocelot/Requester/HttpExeptionToErrorMapper.cs
+++ b/src/Ocelot/Requester/HttpExeptionToErrorMapper.cs
@@ -1,6 +1,6 @@
namespace Ocelot.Requester
{
- using Errors;
+ using Ocelot.Errors;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
@@ -25,7 +25,7 @@ namespace Ocelot.Requester
return _mappers[type](exception);
}
- if (type == typeof(OperationCanceledException))
+ if (type == typeof(OperationCanceledException) || type.IsSubclassOf(typeof(OperationCanceledException)))
{
return new RequestCanceledError(exception.Message);
}
diff --git a/src/Ocelot/Requester/Middleware/HttpRequesterMiddleware.cs b/src/Ocelot/Requester/Middleware/HttpRequesterMiddleware.cs
index 92d3128e..a48aa84d 100644
--- a/src/Ocelot/Requester/Middleware/HttpRequesterMiddleware.cs
+++ b/src/Ocelot/Requester/Middleware/HttpRequesterMiddleware.cs
@@ -1,6 +1,9 @@
+using System.Net;
+using System.Net.Http;
using Ocelot.Logging;
using Ocelot.Middleware;
using System.Threading.Tasks;
+using Ocelot.Responses;
namespace Ocelot.Requester.Middleware
{
@@ -22,6 +25,8 @@ namespace Ocelot.Requester.Middleware
{
var response = await _requester.GetResponse(context);
+ CreateLogBasedOnResponse(response);
+
if (response.IsError)
{
Logger.LogDebug("IHttpRequester returned an error, setting pipeline error");
@@ -36,5 +41,19 @@ namespace Ocelot.Requester.Middleware
await _next.Invoke(context);
}
+
+ private void CreateLogBasedOnResponse(Response response)
+ {
+ if (response.Data?.StatusCode <= HttpStatusCode.BadRequest)
+ {
+ Logger.LogInformation(
+ $"{(int)response.Data.StatusCode} ({response.Data.ReasonPhrase}) status code, request uri: {response.Data.RequestMessage?.RequestUri}");
+ }
+ else if (response.Data?.StatusCode >= HttpStatusCode.BadRequest)
+ {
+ Logger.LogWarning(
+ $"{(int) response.Data.StatusCode} ({response.Data.ReasonPhrase}) status code, request uri: {response.Data.RequestMessage?.RequestUri}");
+ }
+ }
}
}
diff --git a/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs b/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs
index 1863a206..7a580872 100644
--- a/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs
+++ b/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs
@@ -34,7 +34,7 @@ namespace Ocelot.ServiceDiscovery
foreach (var downstreamAddress in reRoute.DownstreamAddresses)
{
- var service = new Service(reRoute.ServiceName, new ServiceHostAndPort(downstreamAddress.Host, downstreamAddress.Port), string.Empty, string.Empty, new string[0]);
+ var service = new Service(reRoute.ServiceName, new ServiceHostAndPort(downstreamAddress.Host, downstreamAddress.Port, reRoute.DownstreamScheme), string.Empty, string.Empty, new string[0]);
services.Add(service);
}
diff --git a/src/Ocelot/Values/ServiceHostAndPort.cs b/src/Ocelot/Values/ServiceHostAndPort.cs
index 4a8e3748..fff7edba 100644
--- a/src/Ocelot/Values/ServiceHostAndPort.cs
+++ b/src/Ocelot/Values/ServiceHostAndPort.cs
@@ -8,8 +8,13 @@
DownstreamPort = downstreamPort;
}
+ public ServiceHostAndPort(string downstreamHost, int downstreamPort, string scheme)
+ : this(downstreamHost, downstreamPort) => Scheme = scheme;
+
public string DownstreamHost { get; }
- public int DownstreamPort { get; }
+ public int DownstreamPort { get; }
+
+ public string Scheme { get; }
}
}
diff --git a/test/Ocelot.AcceptanceTests/AggregateTests.cs b/test/Ocelot.AcceptanceTests/AggregateTests.cs
index 21573f61..2bd2f09f 100644
--- a/test/Ocelot.AcceptanceTests/AggregateTests.cs
+++ b/test/Ocelot.AcceptanceTests/AggregateTests.cs
@@ -1,656 +1,656 @@
-using Microsoft.AspNetCore.Http;
-using Ocelot.Configuration.File;
-using Ocelot.Middleware;
-using Ocelot.Middleware.Multiplexer;
-using Shouldly;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Net;
-using System.Net.Http;
-using System.Threading.Tasks;
-using TestStack.BDDfy;
-using Xunit;
-
-namespace Ocelot.AcceptanceTests
-{
- public class AggregateTests : IDisposable
- {
- private readonly Steps _steps;
- private string _downstreamPathOne;
- private string _downstreamPathTwo;
- private readonly ServiceHandler _serviceHandler;
-
- public AggregateTests()
- {
- _serviceHandler = new ServiceHandler();
- _steps = new Steps();
- }
-
- [Fact]
- public void should_fix_issue_597()
- {
- var configuration = new FileConfiguration
- {
- ReRoutes = new List
- {
- new FileReRoute
- {
- DownstreamPathTemplate = "/api/values?MailId={userid}",
- UpstreamPathTemplate = "/key1data/{userid}",
- UpstreamHttpMethod = new List {"Get"},
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 8571
- }
- },
- Key = "key1"
- },
- new FileReRoute
- {
- DownstreamPathTemplate = "/api/values?MailId={userid}",
- UpstreamPathTemplate = "/key2data/{userid}",
- UpstreamHttpMethod = new List {"Get"},
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 8571
- }
- },
- Key = "key2"
- },
- new FileReRoute
- {
- DownstreamPathTemplate = "/api/values?MailId={userid}",
- UpstreamPathTemplate = "/key3data/{userid}",
- UpstreamHttpMethod = new List {"Get"},
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 8571
- }
- },
- Key = "key3"
- },
- new FileReRoute
- {
- DownstreamPathTemplate = "/api/values?MailId={userid}",
- UpstreamPathTemplate = "/key4data/{userid}",
- UpstreamHttpMethod = new List {"Get"},
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 8571
- }
- },
- Key = "key4"
- },
- },
- Aggregates = new List
- {
- new FileAggregateReRoute
- {
- ReRouteKeys = new List{
- "key1",
- "key2",
- "key3",
- "key4"
- },
- UpstreamPathTemplate = "/EmpDetail/IN/{userid}"
- },
- new FileAggregateReRoute
- {
- ReRouteKeys = new List{
- "key1",
- "key2",
- },
- UpstreamPathTemplate = "/EmpDetail/US/{userid}"
- }
- },
- GlobalConfiguration = new FileGlobalConfiguration
- {
- RequestIdKey = "CorrelationID"
- }
- };
-
- var expected = "{\"key1\":some_data,\"key2\":some_data}";
-
- this.Given(x => x.GivenServiceIsRunning("http://localhost:8571", 200, "some_data"))
- .And(x => _steps.GivenThereIsAConfiguration(configuration))
- .And(x => _steps.GivenOcelotIsRunning())
- .When(x => _steps.WhenIGetUrlOnTheApiGateway("/EmpDetail/US/1"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .And(x => _steps.ThenTheResponseBodyShouldBe(expected))
- .BDDfy();
- }
-
- [Fact]
- public void should_return_response_200_with_advanced_aggregate_configs()
- {
- var configuration = new FileConfiguration
- {
- ReRoutes = new List
- {
- new FileReRoute
- {
- DownstreamPathTemplate = "/",
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 51889,
- }
- },
- UpstreamPathTemplate = "/Comments",
- UpstreamHttpMethod = new List { "Get" },
- Key = "Comments"
- },
- new FileReRoute
- {
- DownstreamPathTemplate = "/users/{userId}",
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 51890,
- }
- },
- UpstreamPathTemplate = "/UserDetails",
- UpstreamHttpMethod = new List { "Get" },
- Key = "UserDetails"
- },
- new FileReRoute
- {
- DownstreamPathTemplate = "/posts/{postId}",
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 51887,
- }
- },
- UpstreamPathTemplate = "/PostDetails",
- UpstreamHttpMethod = new List { "Get" },
- Key = "PostDetails"
- }
- },
- Aggregates = new List
- {
- new FileAggregateReRoute
- {
- UpstreamPathTemplate = "/",
- UpstreamHost = "localhost",
- ReRouteKeys = new List
- {
- "Comments",
- "UserDetails",
- "PostDetails"
- },
- ReRouteKeysConfig = new List()
- {
- new AggregateReRouteConfig(){ReRouteKey = "UserDetails",JsonPath = "$[*].writerId",Parameter = "userId"},
- new AggregateReRouteConfig(){ReRouteKey = "PostDetails",JsonPath = "$[*].postId",Parameter = "postId"}
- },
- }
- }
- };
-
- var userDetailsResponseContent = @"{""id"":1,""firstName"":""abolfazl"",""lastName"":""rajabpour""}";
- var postDetailsResponseContent = @"{""id"":1,""title"":""post1""}";
- var commentsResponseContent = @"[{""id"":1,""writerId"":1,""postId"":2,""text"":""text1""},{""id"":2,""writerId"":1,""postId"":2,""text"":""text2""}]";
-
- var expected = "{\"Comments\":" + commentsResponseContent + ",\"UserDetails\":" + userDetailsResponseContent + ",\"PostDetails\":" + postDetailsResponseContent + "}";
-
- this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51889", "/", 200, commentsResponseContent))
- .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51890", "/users/1", 200, userDetailsResponseContent))
- .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51887", "/posts/2", 200, postDetailsResponseContent))
- .And(x => _steps.GivenThereIsAConfiguration(configuration))
- .And(x => _steps.GivenOcelotIsRunning())
- .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .And(x => _steps.ThenTheResponseBodyShouldBe(expected))
- .BDDfy();
- }
-
- [Fact]
- public void should_return_response_200_with_simple_url_user_defined_aggregate()
- {
- var configuration = new FileConfiguration
- {
- ReRoutes = new List
- {
- new FileReRoute
- {
- DownstreamPathTemplate = "/",
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 51885,
- }
- },
- UpstreamPathTemplate = "/laura",
- UpstreamHttpMethod = new List { "Get" },
- Key = "Laura"
- },
- new FileReRoute
- {
- DownstreamPathTemplate = "/",
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 51886,
- }
- },
- UpstreamPathTemplate = "/tom",
- UpstreamHttpMethod = new List { "Get" },
- Key = "Tom"
- }
- },
- Aggregates = new List
- {
- new FileAggregateReRoute
- {
- UpstreamPathTemplate = "/",
- UpstreamHost = "localhost",
- ReRouteKeys = new List
- {
- "Laura",
- "Tom"
- },
- Aggregator = "FakeDefinedAggregator"
- }
- }
- };
-
- var expected = "Bye from Laura, Bye from Tom";
-
- this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51885", "/", 200, "{Hello from Laura}"))
- .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51886", "/", 200, "{Hello from Tom}"))
- .And(x => _steps.GivenThereIsAConfiguration(configuration))
- .And(x => _steps.GivenOcelotIsRunningWithSpecficAggregatorsRegisteredInDi())
- .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .And(x => _steps.ThenTheResponseBodyShouldBe(expected))
- .And(x => ThenTheDownstreamUrlPathShouldBe("/", "/"))
- .BDDfy();
- }
-
- [Fact]
- public void should_return_response_200_with_simple_url()
- {
- var configuration = new FileConfiguration
- {
- ReRoutes = new List
- {
- new FileReRoute
- {
- DownstreamPathTemplate = "/",
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 51875,
- }
- },
- UpstreamPathTemplate = "/laura",
- UpstreamHttpMethod = new List { "Get" },
- Key = "Laura"
- },
- new FileReRoute
- {
- DownstreamPathTemplate = "/",
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 51886,
- }
- },
- UpstreamPathTemplate = "/tom",
- UpstreamHttpMethod = new List { "Get" },
- Key = "Tom"
- }
- },
- Aggregates = new List
- {
- new FileAggregateReRoute
- {
- UpstreamPathTemplate = "/",
- UpstreamHost = "localhost",
- ReRouteKeys = new List
- {
- "Laura",
- "Tom"
- }
- }
- }
- };
-
- var expected = "{\"Laura\":{Hello from Laura},\"Tom\":{Hello from Tom}}";
-
- this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51875", "/", 200, "{Hello from Laura}"))
- .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51886", "/", 200, "{Hello from Tom}"))
- .And(x => _steps.GivenThereIsAConfiguration(configuration))
- .And(x => _steps.GivenOcelotIsRunning())
- .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .And(x => _steps.ThenTheResponseBodyShouldBe(expected))
- .And(x => ThenTheDownstreamUrlPathShouldBe("/", "/"))
- .BDDfy();
- }
-
- [Fact]
- public void should_return_response_200_with_simple_url_one_service_404()
- {
- var configuration = new FileConfiguration
- {
- ReRoutes = new List
- {
- new FileReRoute
- {
- DownstreamPathTemplate = "/",
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 51881,
- }
- },
- UpstreamPathTemplate = "/laura",
- UpstreamHttpMethod = new List { "Get" },
- Key = "Laura"
- },
- new FileReRoute
- {
- DownstreamPathTemplate = "/",
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 51882,
- }
- },
- UpstreamPathTemplate = "/tom",
- UpstreamHttpMethod = new List { "Get" },
- Key = "Tom"
- }
- },
- Aggregates = new List
- {
- new FileAggregateReRoute
- {
- UpstreamPathTemplate = "/",
- UpstreamHost = "localhost",
- ReRouteKeys = new List
- {
- "Laura",
- "Tom"
- }
- }
- }
- };
-
- var expected = "{\"Laura\":,\"Tom\":{Hello from Tom}}";
-
- this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51881", "/", 404, ""))
- .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51882", "/", 200, "{Hello from Tom}"))
- .And(x => _steps.GivenThereIsAConfiguration(configuration))
- .And(x => _steps.GivenOcelotIsRunning())
- .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .And(x => _steps.ThenTheResponseBodyShouldBe(expected))
- .And(x => ThenTheDownstreamUrlPathShouldBe("/", "/"))
- .BDDfy();
- }
-
- [Fact]
- public void should_return_response_200_with_simple_url_both_service_404()
- {
- var configuration = new FileConfiguration
- {
- ReRoutes = new List
- {
- new FileReRoute
- {
- DownstreamPathTemplate = "/",
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 51883,
- }
- },
- UpstreamPathTemplate = "/laura",
- UpstreamHttpMethod = new List { "Get" },
- Key = "Laura"
- },
- new FileReRoute
- {
- DownstreamPathTemplate = "/",
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 51884,
- }
- },
- UpstreamPathTemplate = "/tom",
- UpstreamHttpMethod = new List { "Get" },
- Key = "Tom"
- }
- },
- Aggregates = new List
- {
- new FileAggregateReRoute
- {
- UpstreamPathTemplate = "/",
- UpstreamHost = "localhost",
- ReRouteKeys = new List
- {
- "Laura",
- "Tom"
- }
- }
- }
- };
-
- var expected = "{\"Laura\":,\"Tom\":}";
-
- this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51883", "/", 404, ""))
- .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51884", "/", 404, ""))
- .And(x => _steps.GivenThereIsAConfiguration(configuration))
- .And(x => _steps.GivenOcelotIsRunning())
- .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .And(x => _steps.ThenTheResponseBodyShouldBe(expected))
- .And(x => ThenTheDownstreamUrlPathShouldBe("/", "/"))
- .BDDfy();
- }
-
- [Fact]
- public void should_be_thread_safe()
- {
- var configuration = new FileConfiguration
- {
- ReRoutes = new List
- {
- new FileReRoute
- {
- DownstreamPathTemplate = "/",
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 51878,
- }
- },
- UpstreamPathTemplate = "/laura",
- UpstreamHttpMethod = new List { "Get" },
- Key = "Laura"
- },
- new FileReRoute
- {
- DownstreamPathTemplate = "/",
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 51880,
- }
- },
- UpstreamPathTemplate = "/tom",
- UpstreamHttpMethod = new List { "Get" },
- Key = "Tom"
- }
- },
- Aggregates = new List
- {
- new FileAggregateReRoute
- {
- UpstreamPathTemplate = "/",
- UpstreamHost = "localhost",
- ReRouteKeys = new List
- {
- "Laura",
- "Tom"
- }
- }
- }
- };
-
- this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51878", "/", 200, "{Hello from Laura}"))
- .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51880", "/", 200, "{Hello from Tom}"))
- .And(x => _steps.GivenThereIsAConfiguration(configuration))
- .And(x => _steps.GivenOcelotIsRunning())
- .When(x => _steps.WhenIMakeLotsOfDifferentRequestsToTheApiGateway())
- .And(x => ThenTheDownstreamUrlPathShouldBe("/", "/"))
- .BDDfy();
- }
-
- private void GivenServiceIsRunning(string baseUrl, int statusCode, string responseBody)
- {
- _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, async context =>
- {
- context.Response.StatusCode = statusCode;
- await context.Response.WriteAsync(responseBody);
- });
- }
-
- private void GivenServiceOneIsRunning(string baseUrl, string basePath, int statusCode, string responseBody)
- {
- _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
- {
- _downstreamPathOne = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
-
- if (_downstreamPathOne != basePath)
- {
- context.Response.StatusCode = statusCode;
- await context.Response.WriteAsync("downstream path didnt match base path");
- }
- else
- {
- context.Response.StatusCode = statusCode;
- await context.Response.WriteAsync(responseBody);
- }
- });
- }
-
- private void GivenServiceTwoIsRunning(string baseUrl, string basePath, int statusCode, string responseBody)
- {
- _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
- {
- _downstreamPathTwo = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
-
- if (_downstreamPathTwo != basePath)
- {
- context.Response.StatusCode = statusCode;
- await context.Response.WriteAsync("downstream path didnt match base path");
- }
- else
- {
- context.Response.StatusCode = statusCode;
- await context.Response.WriteAsync(responseBody);
- }
- });
- }
-
- internal void ThenTheDownstreamUrlPathShouldBe(string expectedDownstreamPathOne, string expectedDownstreamPath)
- {
- _downstreamPathOne.ShouldBe(expectedDownstreamPathOne);
- _downstreamPathTwo.ShouldBe(expectedDownstreamPath);
- }
-
- public void Dispose()
- {
- _serviceHandler.Dispose();
- _steps.Dispose();
- }
- }
-
- public class FakeDepdendency
- {
- }
-
- public class FakeDefinedAggregator : IDefinedAggregator
- {
- private readonly FakeDepdendency _dep;
-
- public FakeDefinedAggregator(FakeDepdendency dep)
- {
- _dep = dep;
- }
-
- public async Task Aggregate(List responses)
- {
- var one = await responses[0].DownstreamResponse.Content.ReadAsStringAsync();
- var two = await responses[1].DownstreamResponse.Content.ReadAsStringAsync();
-
- var merge = $"{one}, {two}";
- merge = merge.Replace("Hello", "Bye").Replace("{", "").Replace("}", "");
- var headers = responses.SelectMany(x => x.DownstreamResponse.Headers).ToList();
- return new DownstreamResponse(new StringContent(merge), HttpStatusCode.OK, headers, "some reason");
- }
- }
-}
+using Microsoft.AspNetCore.Http;
+using Ocelot.Configuration.File;
+using Ocelot.Middleware;
+using Ocelot.Middleware.Multiplexer;
+using Shouldly;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+using TestStack.BDDfy;
+using Xunit;
+
+namespace Ocelot.AcceptanceTests
+{
+ public class AggregateTests : IDisposable
+ {
+ private readonly Steps _steps;
+ private string _downstreamPathOne;
+ private string _downstreamPathTwo;
+ private readonly ServiceHandler _serviceHandler;
+
+ public AggregateTests()
+ {
+ _serviceHandler = new ServiceHandler();
+ _steps = new Steps();
+ }
+
+ [Fact]
+ public void should_fix_issue_597()
+ {
+ var configuration = new FileConfiguration
+ {
+ ReRoutes = new List
+ {
+ new FileReRoute
+ {
+ DownstreamPathTemplate = "/api/values?MailId={userid}",
+ UpstreamPathTemplate = "/key1data/{userid}",
+ UpstreamHttpMethod = new List {"Get"},
+ DownstreamScheme = "http",
+ DownstreamHostAndPorts = new List
+ {
+ new FileHostAndPort
+ {
+ Host = "localhost",
+ Port = 8571
+ }
+ },
+ Key = "key1"
+ },
+ new FileReRoute
+ {
+ DownstreamPathTemplate = "/api/values?MailId={userid}",
+ UpstreamPathTemplate = "/key2data/{userid}",
+ UpstreamHttpMethod = new List {"Get"},
+ DownstreamScheme = "http",
+ DownstreamHostAndPorts = new List
+ {
+ new FileHostAndPort
+ {
+ Host = "localhost",
+ Port = 8571
+ }
+ },
+ Key = "key2"
+ },
+ new FileReRoute
+ {
+ DownstreamPathTemplate = "/api/values?MailId={userid}",
+ UpstreamPathTemplate = "/key3data/{userid}",
+ UpstreamHttpMethod = new List {"Get"},
+ DownstreamScheme = "http",
+ DownstreamHostAndPorts = new List
+ {
+ new FileHostAndPort
+ {
+ Host = "localhost",
+ Port = 8571
+ }
+ },
+ Key = "key3"
+ },
+ new FileReRoute
+ {
+ DownstreamPathTemplate = "/api/values?MailId={userid}",
+ UpstreamPathTemplate = "/key4data/{userid}",
+ UpstreamHttpMethod = new List {"Get"},
+ DownstreamScheme = "http",
+ DownstreamHostAndPorts = new List
+ {
+ new FileHostAndPort
+ {
+ Host = "localhost",
+ Port = 8571
+ }
+ },
+ Key = "key4"
+ },
+ },
+ Aggregates = new List
+ {
+ new FileAggregateReRoute
+ {
+ ReRouteKeys = new List{
+ "key1",
+ "key2",
+ "key3",
+ "key4"
+ },
+ UpstreamPathTemplate = "/EmpDetail/IN/{userid}"
+ },
+ new FileAggregateReRoute
+ {
+ ReRouteKeys = new List{
+ "key1",
+ "key2",
+ },
+ UpstreamPathTemplate = "/EmpDetail/US/{userid}"
+ }
+ },
+ GlobalConfiguration = new FileGlobalConfiguration
+ {
+ RequestIdKey = "CorrelationID"
+ }
+ };
+
+ var expected = "{\"key1\":some_data,\"key2\":some_data}";
+
+ this.Given(x => x.GivenServiceIsRunning("http://localhost:8571", 200, "some_data"))
+ .And(x => _steps.GivenThereIsAConfiguration(configuration))
+ .And(x => _steps.GivenOcelotIsRunning())
+ .When(x => _steps.WhenIGetUrlOnTheApiGateway("/EmpDetail/US/1"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
+ .And(x => _steps.ThenTheResponseBodyShouldBe(expected))
+ .BDDfy();
+ }
+
+ [Fact]
+ public void should_return_response_200_with_advanced_aggregate_configs()
+ {
+ var configuration = new FileConfiguration
+ {
+ ReRoutes = new List
+ {
+ new FileReRoute
+ {
+ DownstreamPathTemplate = "/",
+ DownstreamScheme = "http",
+ DownstreamHostAndPorts = new List
+ {
+ new FileHostAndPort
+ {
+ Host = "localhost",
+ Port = 51889,
+ }
+ },
+ UpstreamPathTemplate = "/Comments",
+ UpstreamHttpMethod = new List { "Get" },
+ Key = "Comments"
+ },
+ new FileReRoute
+ {
+ DownstreamPathTemplate = "/users/{userId}",
+ DownstreamScheme = "http",
+ DownstreamHostAndPorts = new List
+ {
+ new FileHostAndPort
+ {
+ Host = "localhost",
+ Port = 54030,
+ }
+ },
+ UpstreamPathTemplate = "/UserDetails",
+ UpstreamHttpMethod = new List { "Get" },
+ Key = "UserDetails"
+ },
+ new FileReRoute
+ {
+ DownstreamPathTemplate = "/posts/{postId}",
+ DownstreamScheme = "http",
+ DownstreamHostAndPorts = new List
+ {
+ new FileHostAndPort
+ {
+ Host = "localhost",
+ Port = 51887,
+ }
+ },
+ UpstreamPathTemplate = "/PostDetails",
+ UpstreamHttpMethod = new List { "Get" },
+ Key = "PostDetails"
+ }
+ },
+ Aggregates = new List
+ {
+ new FileAggregateReRoute
+ {
+ UpstreamPathTemplate = "/",
+ UpstreamHost = "localhost",
+ ReRouteKeys = new List
+ {
+ "Comments",
+ "UserDetails",
+ "PostDetails"
+ },
+ ReRouteKeysConfig = new List()
+ {
+ new AggregateReRouteConfig(){ReRouteKey = "UserDetails",JsonPath = "$[*].writerId",Parameter = "userId"},
+ new AggregateReRouteConfig(){ReRouteKey = "PostDetails",JsonPath = "$[*].postId",Parameter = "postId"}
+ },
+ }
+ }
+ };
+
+ var userDetailsResponseContent = @"{""id"":1,""firstName"":""abolfazl"",""lastName"":""rajabpour""}";
+ var postDetailsResponseContent = @"{""id"":1,""title"":""post1""}";
+ var commentsResponseContent = @"[{""id"":1,""writerId"":1,""postId"":2,""text"":""text1""},{""id"":2,""writerId"":1,""postId"":2,""text"":""text2""}]";
+
+ var expected = "{\"Comments\":" + commentsResponseContent + ",\"UserDetails\":" + userDetailsResponseContent + ",\"PostDetails\":" + postDetailsResponseContent + "}";
+
+ this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51889", "/", 200, commentsResponseContent))
+ .Given(x => x.GivenServiceTwoIsRunning("http://localhost:54030", "/users/1", 200, userDetailsResponseContent))
+ .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51887", "/posts/2", 200, postDetailsResponseContent))
+ .And(x => _steps.GivenThereIsAConfiguration(configuration))
+ .And(x => _steps.GivenOcelotIsRunning())
+ .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
+ .And(x => _steps.ThenTheResponseBodyShouldBe(expected))
+ .BDDfy();
+ }
+
+ [Fact]
+ public void should_return_response_200_with_simple_url_user_defined_aggregate()
+ {
+ var configuration = new FileConfiguration
+ {
+ ReRoutes = new List
+ {
+ new FileReRoute
+ {
+ DownstreamPathTemplate = "/",
+ DownstreamScheme = "http",
+ DownstreamHostAndPorts = new List
+ {
+ new FileHostAndPort
+ {
+ Host = "localhost",
+ Port = 51885,
+ }
+ },
+ UpstreamPathTemplate = "/laura",
+ UpstreamHttpMethod = new List { "Get" },
+ Key = "Laura"
+ },
+ new FileReRoute
+ {
+ DownstreamPathTemplate = "/",
+ DownstreamScheme = "http",
+ DownstreamHostAndPorts = new List
+ {
+ new FileHostAndPort
+ {
+ Host = "localhost",
+ Port = 51886,
+ }
+ },
+ UpstreamPathTemplate = "/tom",
+ UpstreamHttpMethod = new List { "Get" },
+ Key = "Tom"
+ }
+ },
+ Aggregates = new List
+ {
+ new FileAggregateReRoute
+ {
+ UpstreamPathTemplate = "/",
+ UpstreamHost = "localhost",
+ ReRouteKeys = new List
+ {
+ "Laura",
+ "Tom"
+ },
+ Aggregator = "FakeDefinedAggregator"
+ }
+ }
+ };
+
+ var expected = "Bye from Laura, Bye from Tom";
+
+ this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51885", "/", 200, "{Hello from Laura}"))
+ .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51886", "/", 200, "{Hello from Tom}"))
+ .And(x => _steps.GivenThereIsAConfiguration(configuration))
+ .And(x => _steps.GivenOcelotIsRunningWithSpecficAggregatorsRegisteredInDi())
+ .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
+ .And(x => _steps.ThenTheResponseBodyShouldBe(expected))
+ .And(x => ThenTheDownstreamUrlPathShouldBe("/", "/"))
+ .BDDfy();
+ }
+
+ [Fact]
+ public void should_return_response_200_with_simple_url()
+ {
+ var configuration = new FileConfiguration
+ {
+ ReRoutes = new List
+ {
+ new FileReRoute
+ {
+ DownstreamPathTemplate = "/",
+ DownstreamScheme = "http",
+ DownstreamHostAndPorts = new List
+ {
+ new FileHostAndPort
+ {
+ Host = "localhost",
+ Port = 51875,
+ }
+ },
+ UpstreamPathTemplate = "/laura",
+ UpstreamHttpMethod = new List { "Get" },
+ Key = "Laura"
+ },
+ new FileReRoute
+ {
+ DownstreamPathTemplate = "/",
+ DownstreamScheme = "http",
+ DownstreamHostAndPorts = new List
+ {
+ new FileHostAndPort
+ {
+ Host = "localhost",
+ Port = 52476,
+ }
+ },
+ UpstreamPathTemplate = "/tom",
+ UpstreamHttpMethod = new List { "Get" },
+ Key = "Tom"
+ }
+ },
+ Aggregates = new List
+ {
+ new FileAggregateReRoute
+ {
+ UpstreamPathTemplate = "/",
+ UpstreamHost = "localhost",
+ ReRouteKeys = new List
+ {
+ "Laura",
+ "Tom"
+ }
+ }
+ }
+ };
+
+ var expected = "{\"Laura\":{Hello from Laura},\"Tom\":{Hello from Tom}}";
+
+ this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51875", "/", 200, "{Hello from Laura}"))
+ .Given(x => x.GivenServiceTwoIsRunning("http://localhost:52476", "/", 200, "{Hello from Tom}"))
+ .And(x => _steps.GivenThereIsAConfiguration(configuration))
+ .And(x => _steps.GivenOcelotIsRunning())
+ .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
+ .And(x => _steps.ThenTheResponseBodyShouldBe(expected))
+ .And(x => ThenTheDownstreamUrlPathShouldBe("/", "/"))
+ .BDDfy();
+ }
+
+ [Fact]
+ public void should_return_response_200_with_simple_url_one_service_404()
+ {
+ var configuration = new FileConfiguration
+ {
+ ReRoutes = new List
+ {
+ new FileReRoute
+ {
+ DownstreamPathTemplate = "/",
+ DownstreamScheme = "http",
+ DownstreamHostAndPorts = new List
+ {
+ new FileHostAndPort
+ {
+ Host = "localhost",
+ Port = 51881,
+ }
+ },
+ UpstreamPathTemplate = "/laura",
+ UpstreamHttpMethod = new List { "Get" },
+ Key = "Laura"
+ },
+ new FileReRoute
+ {
+ DownstreamPathTemplate = "/",
+ DownstreamScheme = "http",
+ DownstreamHostAndPorts = new List
+ {
+ new FileHostAndPort
+ {
+ Host = "localhost",
+ Port = 51889,
+ }
+ },
+ UpstreamPathTemplate = "/tom",
+ UpstreamHttpMethod = new List { "Get" },
+ Key = "Tom"
+ }
+ },
+ Aggregates = new List
+ {
+ new FileAggregateReRoute
+ {
+ UpstreamPathTemplate = "/",
+ UpstreamHost = "localhost",
+ ReRouteKeys = new List
+ {
+ "Laura",
+ "Tom"
+ }
+ }
+ }
+ };
+
+ var expected = "{\"Laura\":,\"Tom\":{Hello from Tom}}";
+
+ this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51881", "/", 404, ""))
+ .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51889", "/", 200, "{Hello from Tom}"))
+ .And(x => _steps.GivenThereIsAConfiguration(configuration))
+ .And(x => _steps.GivenOcelotIsRunning())
+ .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
+ .And(x => _steps.ThenTheResponseBodyShouldBe(expected))
+ .And(x => ThenTheDownstreamUrlPathShouldBe("/", "/"))
+ .BDDfy();
+ }
+
+ [Fact]
+ public void should_return_response_200_with_simple_url_both_service_404()
+ {
+ var configuration = new FileConfiguration
+ {
+ ReRoutes = new List
+ {
+ new FileReRoute
+ {
+ DownstreamPathTemplate = "/",
+ DownstreamScheme = "http",
+ DownstreamHostAndPorts = new List
+ {
+ new FileHostAndPort
+ {
+ Host = "localhost",
+ Port = 51883,
+ }
+ },
+ UpstreamPathTemplate = "/laura",
+ UpstreamHttpMethod = new List { "Get" },
+ Key = "Laura"
+ },
+ new FileReRoute
+ {
+ DownstreamPathTemplate = "/",
+ DownstreamScheme = "http",
+ DownstreamHostAndPorts = new List
+ {
+ new FileHostAndPort
+ {
+ Host = "localhost",
+ Port = 51884,
+ }
+ },
+ UpstreamPathTemplate = "/tom",
+ UpstreamHttpMethod = new List { "Get" },
+ Key = "Tom"
+ }
+ },
+ Aggregates = new List
+ {
+ new FileAggregateReRoute
+ {
+ UpstreamPathTemplate = "/",
+ UpstreamHost = "localhost",
+ ReRouteKeys = new List
+ {
+ "Laura",
+ "Tom"
+ }
+ }
+ }
+ };
+
+ var expected = "{\"Laura\":,\"Tom\":}";
+
+ this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51883", "/", 404, ""))
+ .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51884", "/", 404, ""))
+ .And(x => _steps.GivenThereIsAConfiguration(configuration))
+ .And(x => _steps.GivenOcelotIsRunning())
+ .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
+ .And(x => _steps.ThenTheResponseBodyShouldBe(expected))
+ .And(x => ThenTheDownstreamUrlPathShouldBe("/", "/"))
+ .BDDfy();
+ }
+
+ [Fact]
+ public void should_be_thread_safe()
+ {
+ var configuration = new FileConfiguration
+ {
+ ReRoutes = new List
+ {
+ new FileReRoute
+ {
+ DownstreamPathTemplate = "/",
+ DownstreamScheme = "http",
+ DownstreamHostAndPorts = new List
+ {
+ new FileHostAndPort
+ {
+ Host = "localhost",
+ Port = 51878,
+ }
+ },
+ UpstreamPathTemplate = "/laura",
+ UpstreamHttpMethod = new List { "Get" },
+ Key = "Laura"
+ },
+ new FileReRoute
+ {
+ DownstreamPathTemplate = "/",
+ DownstreamScheme = "http",
+ DownstreamHostAndPorts = new List
+ {
+ new FileHostAndPort
+ {
+ Host = "localhost",
+ Port = 51880,
+ }
+ },
+ UpstreamPathTemplate = "/tom",
+ UpstreamHttpMethod = new List { "Get" },
+ Key = "Tom"
+ }
+ },
+ Aggregates = new List
+ {
+ new FileAggregateReRoute
+ {
+ UpstreamPathTemplate = "/",
+ UpstreamHost = "localhost",
+ ReRouteKeys = new List
+ {
+ "Laura",
+ "Tom"
+ }
+ }
+ }
+ };
+
+ this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51878", "/", 200, "{Hello from Laura}"))
+ .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51880", "/", 200, "{Hello from Tom}"))
+ .And(x => _steps.GivenThereIsAConfiguration(configuration))
+ .And(x => _steps.GivenOcelotIsRunning())
+ .When(x => _steps.WhenIMakeLotsOfDifferentRequestsToTheApiGateway())
+ .And(x => ThenTheDownstreamUrlPathShouldBe("/", "/"))
+ .BDDfy();
+ }
+
+ private void GivenServiceIsRunning(string baseUrl, int statusCode, string responseBody)
+ {
+ _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, async context =>
+ {
+ context.Response.StatusCode = statusCode;
+ await context.Response.WriteAsync(responseBody);
+ });
+ }
+
+ private void GivenServiceOneIsRunning(string baseUrl, string basePath, int statusCode, string responseBody)
+ {
+ _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
+ {
+ _downstreamPathOne = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
+
+ if (_downstreamPathOne != basePath)
+ {
+ context.Response.StatusCode = statusCode;
+ await context.Response.WriteAsync("downstream path didnt match base path");
+ }
+ else
+ {
+ context.Response.StatusCode = statusCode;
+ await context.Response.WriteAsync(responseBody);
+ }
+ });
+ }
+
+ private void GivenServiceTwoIsRunning(string baseUrl, string basePath, int statusCode, string responseBody)
+ {
+ _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
+ {
+ _downstreamPathTwo = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
+
+ if (_downstreamPathTwo != basePath)
+ {
+ context.Response.StatusCode = statusCode;
+ await context.Response.WriteAsync("downstream path didnt match base path");
+ }
+ else
+ {
+ context.Response.StatusCode = statusCode;
+ await context.Response.WriteAsync(responseBody);
+ }
+ });
+ }
+
+ internal void ThenTheDownstreamUrlPathShouldBe(string expectedDownstreamPathOne, string expectedDownstreamPath)
+ {
+ _downstreamPathOne.ShouldBe(expectedDownstreamPathOne);
+ _downstreamPathTwo.ShouldBe(expectedDownstreamPath);
+ }
+
+ public void Dispose()
+ {
+ _serviceHandler.Dispose();
+ _steps.Dispose();
+ }
+ }
+
+ public class FakeDepdendency
+ {
+ }
+
+ public class FakeDefinedAggregator : IDefinedAggregator
+ {
+ private readonly FakeDepdendency _dep;
+
+ public FakeDefinedAggregator(FakeDepdendency dep)
+ {
+ _dep = dep;
+ }
+
+ public async Task Aggregate(List responses)
+ {
+ var one = await responses[0].DownstreamResponse.Content.ReadAsStringAsync();
+ var two = await responses[1].DownstreamResponse.Content.ReadAsStringAsync();
+
+ var merge = $"{one}, {two}";
+ merge = merge.Replace("Hello", "Bye").Replace("{", "").Replace("}", "");
+ var headers = responses.SelectMany(x => x.DownstreamResponse.Headers).ToList();
+ return new DownstreamResponse(new StringContent(merge), HttpStatusCode.OK, headers, "some reason");
+ }
+ }
+}
diff --git a/test/Ocelot.AcceptanceTests/CachingTests.cs b/test/Ocelot.AcceptanceTests/CachingTests.cs
index dc0bd99d..d8b1a05b 100644
--- a/test/Ocelot.AcceptanceTests/CachingTests.cs
+++ b/test/Ocelot.AcceptanceTests/CachingTests.cs
@@ -1,225 +1,225 @@
-namespace Ocelot.AcceptanceTests
-{
- using Configuration.File;
- using Microsoft.AspNetCore.Http;
- using System;
- using System.Collections.Generic;
- using System.Net;
- using System.Threading;
- using TestStack.BDDfy;
- using Xunit;
-
- public class CachingTests : IDisposable
- {
- private readonly Steps _steps;
- private readonly ServiceHandler _serviceHandler;
-
- public CachingTests()
- {
- _serviceHandler = new ServiceHandler();
- _steps = new Steps();
- }
-
- [Fact]
- public void should_return_cached_response()
- {
- var configuration = new FileConfiguration
- {
- ReRoutes = new List
- {
- new FileReRoute
- {
- DownstreamPathTemplate = "/",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 51899,
- }
- },
- DownstreamScheme = "http",
- UpstreamPathTemplate = "/",
- UpstreamHttpMethod = new List { "Get" },
- FileCacheOptions = new FileCacheOptions
- {
- TtlSeconds = 100
- }
- }
- }
- };
-
- this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51899", 200, "Hello from Laura", null, null))
- .And(x => _steps.GivenThereIsAConfiguration(configuration))
- .And(x => _steps.GivenOcelotIsRunning())
- .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
- .Given(x => x.GivenTheServiceNowReturns("http://localhost:51899", 200, "Hello from Tom"))
- .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
- .And(x => _steps.ThenTheContentLengthIs(16))
- .BDDfy();
- }
-
- [Fact]
- public void should_return_cached_response_with_expires_header()
- {
- var configuration = new FileConfiguration
- {
- ReRoutes = new List
- {
- new FileReRoute
- {
- DownstreamPathTemplate = "/",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 52839,
- }
- },
- DownstreamScheme = "http",
- UpstreamPathTemplate = "/",
- UpstreamHttpMethod = new List { "Get" },
- FileCacheOptions = new FileCacheOptions
- {
- TtlSeconds = 100
- }
- }
- }
- };
-
- this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:52839", 200, "Hello from Laura", "Expires", "-1"))
- .And(x => _steps.GivenThereIsAConfiguration(configuration))
- .And(x => _steps.GivenOcelotIsRunning())
- .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
- .Given(x => x.GivenTheServiceNowReturns("http://localhost:52839", 200, "Hello from Tom"))
- .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
- .And(x => _steps.ThenTheContentLengthIs(16))
- .And(x => _steps.ThenTheResponseBodyHeaderIs("Expires", "-1"))
- .BDDfy();
- }
-
- [Fact]
- public void should_return_cached_response_when_using_jsonserialized_cache()
- {
- var configuration = new FileConfiguration
- {
- ReRoutes = new List
- {
- new FileReRoute
- {
- DownstreamPathTemplate = "/",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 51899,
- }
- },
- DownstreamScheme = "http",
- UpstreamPathTemplate = "/",
- UpstreamHttpMethod = new List { "Get" },
- FileCacheOptions = new FileCacheOptions
- {
- TtlSeconds = 100
- }
- }
- }
- };
-
- this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51899", 200, "Hello from Laura", null, null))
- .And(x => _steps.GivenThereIsAConfiguration(configuration))
- .And(x => _steps.GivenOcelotIsRunningUsingJsonSerializedCache())
- .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
- .Given(x => x.GivenTheServiceNowReturns("http://localhost:51899", 200, "Hello from Tom"))
- .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
- .BDDfy();
- }
-
- [Fact]
- public void should_not_return_cached_response_as_ttl_expires()
- {
- var configuration = new FileConfiguration
- {
- ReRoutes = new List
- {
- new FileReRoute
- {
- DownstreamPathTemplate = "/",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 51899,
- }
- },
- DownstreamScheme = "http",
- UpstreamPathTemplate = "/",
- UpstreamHttpMethod = new List { "Get" },
- FileCacheOptions = new FileCacheOptions
- {
- TtlSeconds = 1
- }
- }
- }
- };
-
- this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51899", 200, "Hello from Laura", null, null))
- .And(x => _steps.GivenThereIsAConfiguration(configuration))
- .And(x => _steps.GivenOcelotIsRunning())
- .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
- .Given(x => x.GivenTheServiceNowReturns("http://localhost:51899", 200, "Hello from Tom"))
- .And(x => x.GivenTheCacheExpires())
- .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Tom"))
- .BDDfy();
- }
-
- private void GivenTheCacheExpires()
- {
- Thread.Sleep(1000);
- }
-
- private void GivenTheServiceNowReturns(string url, int statusCode, string responseBody)
- {
- _serviceHandler.Dispose();
- GivenThereIsAServiceRunningOn(url, statusCode, responseBody, null, null);
- }
-
- private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody, string key, string value)
- {
- _serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
- {
- if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(key))
- {
- context.Response.Headers.Add(key, value);
- }
- context.Response.StatusCode = statusCode;
- await context.Response.WriteAsync(responseBody);
- });
- }
-
- public void Dispose()
- {
- _serviceHandler?.Dispose();
- _steps.Dispose();
- }
- }
-}
+namespace Ocelot.AcceptanceTests
+{
+ using Configuration.File;
+ using Microsoft.AspNetCore.Http;
+ using System;
+ using System.Collections.Generic;
+ using System.Net;
+ using System.Threading;
+ using TestStack.BDDfy;
+ using Xunit;
+
+ public class CachingTests : IDisposable
+ {
+ private readonly Steps _steps;
+ private readonly ServiceHandler _serviceHandler;
+
+ public CachingTests()
+ {
+ _serviceHandler = new ServiceHandler();
+ _steps = new Steps();
+ }
+
+ [Fact]
+ public void should_return_cached_response()
+ {
+ var configuration = new FileConfiguration
+ {
+ ReRoutes = new List
+ {
+ new FileReRoute
+ {
+ DownstreamPathTemplate = "/",
+ DownstreamHostAndPorts = new List
+ {
+ new FileHostAndPort
+ {
+ Host = "localhost",
+ Port = 57899,
+ }
+ },
+ DownstreamScheme = "http",
+ UpstreamPathTemplate = "/",
+ UpstreamHttpMethod = new List { "Get" },
+ FileCacheOptions = new FileCacheOptions
+ {
+ TtlSeconds = 100
+ }
+ }
+ }
+ };
+
+ this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:57899", 200, "Hello from Laura", null, null))
+ .And(x => _steps.GivenThereIsAConfiguration(configuration))
+ .And(x => _steps.GivenOcelotIsRunning())
+ .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
+ .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
+ .Given(x => x.GivenTheServiceNowReturns("http://localhost:57899", 200, "Hello from Tom"))
+ .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
+ .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
+ .And(x => _steps.ThenTheContentLengthIs(16))
+ .BDDfy();
+ }
+
+ [Fact]
+ public void should_return_cached_response_with_expires_header()
+ {
+ var configuration = new FileConfiguration
+ {
+ ReRoutes = new List
+ {
+ new FileReRoute
+ {
+ DownstreamPathTemplate = "/",
+ DownstreamHostAndPorts = new List
+ {
+ new FileHostAndPort
+ {
+ Host = "localhost",
+ Port = 52839,
+ }
+ },
+ DownstreamScheme = "http",
+ UpstreamPathTemplate = "/",
+ UpstreamHttpMethod = new List { "Get" },
+ FileCacheOptions = new FileCacheOptions
+ {
+ TtlSeconds = 100
+ }
+ }
+ }
+ };
+
+ this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:52839", 200, "Hello from Laura", "Expires", "-1"))
+ .And(x => _steps.GivenThereIsAConfiguration(configuration))
+ .And(x => _steps.GivenOcelotIsRunning())
+ .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
+ .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
+ .Given(x => x.GivenTheServiceNowReturns("http://localhost:52839", 200, "Hello from Tom"))
+ .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
+ .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
+ .And(x => _steps.ThenTheContentLengthIs(16))
+ .And(x => _steps.ThenTheResponseBodyHeaderIs("Expires", "-1"))
+ .BDDfy();
+ }
+
+ [Fact]
+ public void should_return_cached_response_when_using_jsonserialized_cache()
+ {
+ var configuration = new FileConfiguration
+ {
+ ReRoutes = new List
+ {
+ new FileReRoute
+ {
+ DownstreamPathTemplate = "/",
+ DownstreamHostAndPorts = new List
+ {
+ new FileHostAndPort
+ {
+ Host = "localhost",
+ Port = 57879,
+ }
+ },
+ DownstreamScheme = "http",
+ UpstreamPathTemplate = "/",
+ UpstreamHttpMethod = new List { "Get" },
+ FileCacheOptions = new FileCacheOptions
+ {
+ TtlSeconds = 100
+ }
+ }
+ }
+ };
+
+ this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:57879", 200, "Hello from Laura", null, null))
+ .And(x => _steps.GivenThereIsAConfiguration(configuration))
+ .And(x => _steps.GivenOcelotIsRunningUsingJsonSerializedCache())
+ .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
+ .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
+ .Given(x => x.GivenTheServiceNowReturns("http://localhost:57879", 200, "Hello from Tom"))
+ .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
+ .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
+ .BDDfy();
+ }
+
+ [Fact]
+ public void should_not_return_cached_response_as_ttl_expires()
+ {
+ var configuration = new FileConfiguration
+ {
+ ReRoutes = new List
+ {
+ new FileReRoute
+ {
+ DownstreamPathTemplate = "/",
+ DownstreamHostAndPorts = new List
+ {
+ new FileHostAndPort
+ {
+ Host = "localhost",
+ Port = 57873,
+ }
+ },
+ DownstreamScheme = "http",
+ UpstreamPathTemplate = "/",
+ UpstreamHttpMethod = new List { "Get" },
+ FileCacheOptions = new FileCacheOptions
+ {
+ TtlSeconds = 1
+ }
+ }
+ }
+ };
+
+ this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:57873", 200, "Hello from Laura", null, null))
+ .And(x => _steps.GivenThereIsAConfiguration(configuration))
+ .And(x => _steps.GivenOcelotIsRunning())
+ .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
+ .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
+ .Given(x => x.GivenTheServiceNowReturns("http://localhost:57873", 200, "Hello from Tom"))
+ .And(x => x.GivenTheCacheExpires())
+ .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
+ .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Tom"))
+ .BDDfy();
+ }
+
+ private void GivenTheCacheExpires()
+ {
+ Thread.Sleep(1000);
+ }
+
+ private void GivenTheServiceNowReturns(string url, int statusCode, string responseBody)
+ {
+ _serviceHandler.Dispose();
+ GivenThereIsAServiceRunningOn(url, statusCode, responseBody, null, null);
+ }
+
+ private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody, string key, string value)
+ {
+ _serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
+ {
+ if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(key))
+ {
+ context.Response.Headers.Add(key, value);
+ }
+ context.Response.StatusCode = statusCode;
+ await context.Response.WriteAsync(responseBody);
+ });
+ }
+
+ public void Dispose()
+ {
+ _serviceHandler?.Dispose();
+ _steps.Dispose();
+ }
+ }
+}
diff --git a/test/Ocelot.AcceptanceTests/GzipTests.cs b/test/Ocelot.AcceptanceTests/GzipTests.cs
index 51ab1723..a2b1e1b7 100644
--- a/test/Ocelot.AcceptanceTests/GzipTests.cs
+++ b/test/Ocelot.AcceptanceTests/GzipTests.cs
@@ -39,7 +39,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
- Port = 51879,
+ Port = 51179,
}
},
UpstreamPathTemplate = "/",
@@ -50,7 +50,7 @@ namespace Ocelot.AcceptanceTests
var input = "people";
- this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Hello from Laura", "\"people\""))
+ this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51179", "/", 200, "Hello from Laura", "\"people\""))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenThePostHasGzipContent(input))
diff --git a/test/Ocelot.AcceptanceTests/HeaderTests.cs b/test/Ocelot.AcceptanceTests/HeaderTests.cs
index d3c82307..e7c0c2ca 100644
--- a/test/Ocelot.AcceptanceTests/HeaderTests.cs
+++ b/test/Ocelot.AcceptanceTests/HeaderTests.cs
@@ -1,446 +1,446 @@
-namespace Ocelot.AcceptanceTests
-{
- using Microsoft.AspNetCore.Http;
- using Ocelot.Configuration.File;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Net;
- using System.Threading.Tasks;
- using TestStack.BDDfy;
- using Xunit;
-
- public class HeaderTests : IDisposable
- {
- private int _count;
- private readonly Steps _steps;
- private readonly ServiceHandler _serviceHandler;
-
- public HeaderTests()
- {
- _serviceHandler = new ServiceHandler();
- _steps = new Steps();
- }
-
- [Fact]
- public void should_transform_upstream_header()
- {
- var configuration = new FileConfiguration
- {
- ReRoutes = new List
- {
- new FileReRoute
- {
- DownstreamPathTemplate = "/",
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 51871,
- }
- },
- UpstreamPathTemplate = "/",
- UpstreamHttpMethod = new List { "Get" },
- UpstreamHeaderTransform = new Dictionary
- {
- {"Laz", "D, GP"}
- }
- }
- }
- };
-
- this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51871", "/", 200, "Laz"))
- .And(x => _steps.GivenThereIsAConfiguration(configuration))
- .And(x => _steps.GivenOcelotIsRunning())
- .And(x => _steps.GivenIAddAHeader("Laz", "D"))
- .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .And(x => _steps.ThenTheResponseBodyShouldBe("GP"))
- .BDDfy();
- }
-
- [Fact]
- public void should_transform_downstream_header()
- {
- var configuration = new FileConfiguration
- {
- ReRoutes = new List
- {
- new FileReRoute
- {
- DownstreamPathTemplate = "/",
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 51871,
- }
- },
- UpstreamPathTemplate = "/",
- UpstreamHttpMethod = new List { "Get" },
- DownstreamHeaderTransform = new Dictionary
- {
- {"Location", "http://www.bbc.co.uk/, http://ocelot.com/"}
- }
- }
- }
- };
-
- this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51871", "/", 200, "Location", "http://www.bbc.co.uk/"))
- .And(x => _steps.GivenThereIsAConfiguration(configuration))
- .And(x => _steps.GivenOcelotIsRunning())
- .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .And(x => _steps.ThenTheResponseHeaderIs("Location", "http://ocelot.com/"))
- .BDDfy();
- }
-
- [Fact]
- public void should_fix_issue_190()
- {
- var configuration = new FileConfiguration
- {
- ReRoutes = new List
- {
- new FileReRoute
- {
- DownstreamPathTemplate = "/",
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 6773,
- }
- },
- UpstreamPathTemplate = "/",
- UpstreamHttpMethod = new List { "Get" },
- DownstreamHeaderTransform = new Dictionary
- {
- {"Location", "http://localhost:6773, {BaseUrl}"}
- },
- HttpHandlerOptions = new FileHttpHandlerOptions
- {
- AllowAutoRedirect = false
- }
- }
- }
- };
-
- this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6773", "/", 302, "Location", "http://localhost:6773/pay/Receive"))
- .And(x => _steps.GivenThereIsAConfiguration(configuration))
- .And(x => _steps.GivenOcelotIsRunning())
- .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Redirect))
- .And(x => _steps.ThenTheResponseHeaderIs("Location", "http://localhost:5000/pay/Receive"))
- .BDDfy();
- }
-
- [Fact]
- public void should_fix_issue_205()
- {
- var configuration = new FileConfiguration
- {
- ReRoutes = new List
- {
- new FileReRoute
- {
- DownstreamPathTemplate = "/",
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 6773,
- }
- },
- UpstreamPathTemplate = "/",
- UpstreamHttpMethod = new List { "Get" },
- DownstreamHeaderTransform = new Dictionary
- {
- {"Location", "{DownstreamBaseUrl}, {BaseUrl}"}
- },
- HttpHandlerOptions = new FileHttpHandlerOptions
- {
- AllowAutoRedirect = false
- }
- }
- }
- };
-
- this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6773", "/", 302, "Location", "http://localhost:6773/pay/Receive"))
- .And(x => _steps.GivenThereIsAConfiguration(configuration))
- .And(x => _steps.GivenOcelotIsRunning())
- .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Redirect))
- .And(x => _steps.ThenTheResponseHeaderIs("Location", "http://localhost:5000/pay/Receive"))
- .BDDfy();
- }
-
- [Fact]
- public void should_fix_issue_417()
- {
- var configuration = new FileConfiguration
- {
- ReRoutes = new List
- {
- new FileReRoute
- {
- DownstreamPathTemplate = "/",
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 6773,
- }
- },
- UpstreamPathTemplate = "/",
- UpstreamHttpMethod = new List { "Get" },
- DownstreamHeaderTransform = new Dictionary
- {
- {"Location", "{DownstreamBaseUrl}, {BaseUrl}"}
- },
- HttpHandlerOptions = new FileHttpHandlerOptions
- {
- AllowAutoRedirect = false
- }
- }
- },
- GlobalConfiguration = new FileGlobalConfiguration
- {
- BaseUrl = "http://anotherapp.azurewebsites.net"
- }
- };
-
- this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6773", "/", 302, "Location", "http://localhost:6773/pay/Receive"))
- .And(x => _steps.GivenThereIsAConfiguration(configuration))
- .And(x => _steps.GivenOcelotIsRunning())
- .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Redirect))
- .And(x => _steps.ThenTheResponseHeaderIs("Location", "http://anotherapp.azurewebsites.net/pay/Receive"))
- .BDDfy();
- }
-
- [Fact]
- public void request_should_reuse_cookies_with_cookie_container()
- {
- var configuration = new FileConfiguration
- {
- ReRoutes = new List
- {
- new FileReRoute
- {
- DownstreamPathTemplate = "/sso/{everything}",
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 6774,
- }
- },
- UpstreamPathTemplate = "/sso/{everything}",
- UpstreamHttpMethod = new List { "Get", "Post", "Options" },
- HttpHandlerOptions = new FileHttpHandlerOptions
- {
- UseCookieContainer = true
- }
- }
- }
- };
-
- this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6774", "/sso/test", 200))
- .And(x => _steps.GivenThereIsAConfiguration(configuration))
- .And(x => _steps.GivenOcelotIsRunning())
- .And(x => _steps.WhenIGetUrlOnTheApiGateway("/sso/test"))
- .And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .And(x => _steps.ThenTheResponseHeaderIs("Set-Cookie", "test=0; path=/"))
- .And(x => _steps.GivenIAddCookieToMyRequest("test=1; path=/"))
- .When(x => _steps.WhenIGetUrlOnTheApiGateway("/sso/test"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .BDDfy();
- }
-
- [Fact]
- public void request_should_have_own_cookies_no_cookie_container()
- {
- var configuration = new FileConfiguration
- {
- ReRoutes = new List
- {
- new FileReRoute
- {
- DownstreamPathTemplate = "/sso/{everything}",
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 6775,
- }
- },
- UpstreamPathTemplate = "/sso/{everything}",
- UpstreamHttpMethod = new List { "Get", "Post", "Options" },
- HttpHandlerOptions = new FileHttpHandlerOptions
- {
- UseCookieContainer = false
- }
- }
- }
- };
-
- this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6775", "/sso/test", 200))
- .And(x => _steps.GivenThereIsAConfiguration(configuration))
- .And(x => _steps.GivenOcelotIsRunning())
- .And(x => _steps.WhenIGetUrlOnTheApiGateway("/sso/test"))
- .And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .And(x => _steps.ThenTheResponseHeaderIs("Set-Cookie", "test=0; path=/"))
- .And(x => _steps.GivenIAddCookieToMyRequest("test=1; path=/"))
- .When(x => _steps.WhenIGetUrlOnTheApiGateway("/sso/test"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .BDDfy();
- }
-
- [Fact]
- public void issue_474_should_not_put_spaces_in_header()
- {
- var configuration = new FileConfiguration
- {
- ReRoutes = new List
- {
- new FileReRoute
- {
- DownstreamPathTemplate = "/",
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 51879,
- }
- },
- UpstreamPathTemplate = "/",
- UpstreamHttpMethod = new List { "Get" },
- }
- }
- };
-
- this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Accept"))
- .And(x => _steps.GivenThereIsAConfiguration(configuration))
- .And(x => _steps.GivenOcelotIsRunning())
- .And(x => _steps.GivenIAddAHeader("Accept", "text/html,application/xhtml+xml,application/xml;"))
- .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .And(x => _steps.ThenTheResponseBodyShouldBe("text/html,application/xhtml+xml,application/xml;"))
- .BDDfy();
- }
-
- [Fact]
- public void issue_474_should_put_spaces_in_header()
- {
- var configuration = new FileConfiguration
- {
- ReRoutes = new List
- {
- new FileReRoute
- {
- DownstreamPathTemplate = "/",
- DownstreamScheme = "http",
- DownstreamHostAndPorts = new List
- {
- new FileHostAndPort
- {
- Host = "localhost",
- Port = 51879,
- }
- },
- UpstreamPathTemplate = "/",
- UpstreamHttpMethod = new List { "Get" },
- }
- }
- };
-
- this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Accept"))
- .And(x => _steps.GivenThereIsAConfiguration(configuration))
- .And(x => _steps.GivenOcelotIsRunning())
- .And(x => _steps.GivenIAddAHeader("Accept", "text/html"))
- .And(x => _steps.GivenIAddAHeader("Accept", "application/xhtml+xml"))
- .And(x => _steps.GivenIAddAHeader("Accept", "application/xml"))
- .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
- .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
- .And(x => _steps.ThenTheResponseBodyShouldBe("text/html, application/xhtml+xml, application/xml"))
- .BDDfy();
- }
-
- private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode)
- {
- _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, context =>
- {
- if (_count == 0)
- {
- context.Response.Cookies.Append("test", "0");
- _count++;
- context.Response.StatusCode = statusCode;
- return Task.CompletedTask;
- }
-
- if (context.Request.Cookies.TryGetValue("test", out var cookieValue) || context.Request.Headers.TryGetValue("Set-Cookie", out var headerValue))
- {
- if (cookieValue == "0" || headerValue == "test=1; path=/")
- {
- context.Response.StatusCode = statusCode;
- return Task.CompletedTask;
- }
- }
-
- context.Response.StatusCode = 500;
- return Task.CompletedTask;
- });
- }
-
- private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string headerKey)
- {
- _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
- {
- if (context.Request.Headers.TryGetValue(headerKey, out var values))
- {
- var result = values.First();
- context.Response.StatusCode = statusCode;
- await context.Response.WriteAsync(result);
- }
- });
- }
-
- private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string headerKey, string headerValue)
- {
- _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, context =>
- {
- context.Response.OnStarting(() =>
- {
- context.Response.Headers.Add(headerKey, headerValue);
- context.Response.StatusCode = statusCode;
- return Task.CompletedTask;
- });
-
- return Task.CompletedTask;
- });
- }
-
- public void Dispose()
- {
- _serviceHandler?.Dispose();
- _steps.Dispose();
- }
- }
-}
+namespace Ocelot.AcceptanceTests
+{
+ using Microsoft.AspNetCore.Http;
+ using Ocelot.Configuration.File;
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Net;
+ using System.Threading.Tasks;
+ using TestStack.BDDfy;
+ using Xunit;
+
+ public class HeaderTests : IDisposable
+ {
+ private int _count;
+ private readonly Steps _steps;
+ private readonly ServiceHandler _serviceHandler;
+
+ public HeaderTests()
+ {
+ _serviceHandler = new ServiceHandler();
+ _steps = new Steps();
+ }
+
+ [Fact]
+ public void should_transform_upstream_header()
+ {
+ var configuration = new FileConfiguration
+ {
+ ReRoutes = new List
+ {
+ new FileReRoute
+ {
+ DownstreamPathTemplate = "/",
+ DownstreamScheme = "http",
+ DownstreamHostAndPorts = new List
+ {
+ new FileHostAndPort
+ {
+ Host = "localhost",
+ Port = 51871,
+ }
+ },
+ UpstreamPathTemplate = "/",
+ UpstreamHttpMethod = new List { "Get" },
+ UpstreamHeaderTransform = new Dictionary
+ {
+ {"Laz", "D, GP"}
+ }
+ }
+ }
+ };
+
+ this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51871", "/", 200, "Laz"))
+ .And(x => _steps.GivenThereIsAConfiguration(configuration))
+ .And(x => _steps.GivenOcelotIsRunning())
+ .And(x => _steps.GivenIAddAHeader("Laz", "D"))
+ .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
+ .And(x => _steps.ThenTheResponseBodyShouldBe("GP"))
+ .BDDfy();
+ }
+
+ [Fact]
+ public void should_transform_downstream_header()
+ {
+ var configuration = new FileConfiguration
+ {
+ ReRoutes = new List
+ {
+ new FileReRoute
+ {
+ DownstreamPathTemplate = "/",
+ DownstreamScheme = "http",
+ DownstreamHostAndPorts = new List
+ {
+ new FileHostAndPort
+ {
+ Host = "localhost",
+ Port = 51871,
+ }
+ },
+ UpstreamPathTemplate = "/",
+ UpstreamHttpMethod = new List { "Get" },
+ DownstreamHeaderTransform = new Dictionary
+ {
+ {"Location", "http://www.bbc.co.uk/, http://ocelot.com/"}
+ }
+ }
+ }
+ };
+
+ this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51871", "/", 200, "Location", "http://www.bbc.co.uk/"))
+ .And(x => _steps.GivenThereIsAConfiguration(configuration))
+ .And(x => _steps.GivenOcelotIsRunning())
+ .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
+ .And(x => _steps.ThenTheResponseHeaderIs("Location", "http://ocelot.com/"))
+ .BDDfy();
+ }
+
+ [Fact]
+ public void should_fix_issue_190()
+ {
+ var configuration = new FileConfiguration
+ {
+ ReRoutes = new List
+ {
+ new FileReRoute
+ {
+ DownstreamPathTemplate = "/",
+ DownstreamScheme = "http",
+ DownstreamHostAndPorts = new List
+ {
+ new FileHostAndPort
+ {
+ Host = "localhost",
+ Port = 6773,
+ }
+ },
+ UpstreamPathTemplate = "/",
+ UpstreamHttpMethod = new List { "Get" },
+ DownstreamHeaderTransform = new Dictionary
+ {
+ {"Location", "http://localhost:6773, {BaseUrl}"}
+ },
+ HttpHandlerOptions = new FileHttpHandlerOptions
+ {
+ AllowAutoRedirect = false
+ }
+ }
+ }
+ };
+
+ this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6773", "/", 302, "Location", "http://localhost:6773/pay/Receive"))
+ .And(x => _steps.GivenThereIsAConfiguration(configuration))
+ .And(x => _steps.GivenOcelotIsRunning())
+ .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Redirect))
+ .And(x => _steps.ThenTheResponseHeaderIs("Location", "http://localhost:5000/pay/Receive"))
+ .BDDfy();
+ }
+
+ [Fact]
+ public void should_fix_issue_205()
+ {
+ var configuration = new FileConfiguration
+ {
+ ReRoutes = new List
+ {
+ new FileReRoute
+ {
+ DownstreamPathTemplate = "/",
+ DownstreamScheme = "http",
+ DownstreamHostAndPorts = new List
+ {
+ new FileHostAndPort
+ {
+ Host = "localhost",
+ Port = 6773,
+ }
+ },
+ UpstreamPathTemplate = "/",
+ UpstreamHttpMethod = new List { "Get" },
+ DownstreamHeaderTransform = new Dictionary
+ {
+ {"Location", "{DownstreamBaseUrl}, {BaseUrl}"}
+ },
+ HttpHandlerOptions = new FileHttpHandlerOptions
+ {
+ AllowAutoRedirect = false
+ }
+ }
+ }
+ };
+
+ this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6773", "/", 302, "Location", "http://localhost:6773/pay/Receive"))
+ .And(x => _steps.GivenThereIsAConfiguration(configuration))
+ .And(x => _steps.GivenOcelotIsRunning())
+ .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Redirect))
+ .And(x => _steps.ThenTheResponseHeaderIs("Location", "http://localhost:5000/pay/Receive"))
+ .BDDfy();
+ }
+
+ [Fact]
+ public void should_fix_issue_417()
+ {
+ var configuration = new FileConfiguration
+ {
+ ReRoutes = new List
+ {
+ new FileReRoute
+ {
+ DownstreamPathTemplate = "/",
+ DownstreamScheme = "http",
+ DownstreamHostAndPorts = new List
+ {
+ new FileHostAndPort
+ {
+ Host = "localhost",
+ Port = 6773,
+ }
+ },
+ UpstreamPathTemplate = "/",
+ UpstreamHttpMethod = new List { "Get" },
+ DownstreamHeaderTransform = new Dictionary
+ {
+ {"Location", "{DownstreamBaseUrl}, {BaseUrl}"}
+ },
+ HttpHandlerOptions = new FileHttpHandlerOptions
+ {
+ AllowAutoRedirect = false
+ }
+ }
+ },
+ GlobalConfiguration = new FileGlobalConfiguration
+ {
+ BaseUrl = "http://anotherapp.azurewebsites.net"
+ }
+ };
+
+ this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6773", "/", 302, "Location", "http://localhost:6773/pay/Receive"))
+ .And(x => _steps.GivenThereIsAConfiguration(configuration))
+ .And(x => _steps.GivenOcelotIsRunning())
+ .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
+ .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Redirect))
+ .And(x => _steps.ThenTheResponseHeaderIs("Location", "http://anotherapp.azurewebsites.net/pay/Receive"))
+ .BDDfy();
+ }
+
+ [Fact]
+ public void request_should_reuse_cookies_with_cookie_container()
+ {
+ var configuration = new FileConfiguration
+ {
+ ReRoutes = new List