Merge pull request #2 from ThreeMammals/develop

Develop
This commit is contained in:
jlukawska 2020-01-21 16:16:26 +01:00 committed by GitHub
commit 0af49617ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
108 changed files with 5617 additions and 4953 deletions

29
.circleci/config.yml Normal file
View File

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

View File

@ -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/

View File

@ -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 :)

View File

@ -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:

View File

@ -1,7 +1,7 @@
Fixes / New Feature #
## Proposed Changes
-
-
-
Fixes / New Feature #
## Proposed Changes
-
-
-

View File

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

View File

@ -1,16 +1,15 @@
<Project>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/ThreeMammals/Ocelot</RepositoryUrl>
<!-- Optional: Publish the repository URL in the built .nupkg (in the NuSpec <Repository> element) -->
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<!-- Optional: Build symbol package (.snupkg) to distribute the PDB containing Source Link -->
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-18618-05" PrivateAssets="All"/>
</ItemGroup>
</Project>
<Project>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/ThreeMammals/Ocelot</RepositoryUrl>
<!-- Optional: Publish the repository URL in the built .nupkg (in the NuSpec <Repository> element) -->
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<!-- Optional: Build symbol package (.snupkg) to distribute the PDB containing Source Link -->
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
<ItemGroup>
<!-- <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/> -->
</ItemGroup>
</Project>

20
Makefile Normal file
View File

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

View File

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

200
README.md
View File

@ -1,102 +1,98 @@
[<img src="https://threemammals.com/ocelot_logo.png">](https://threemammals.com/ocelot)
[![Build status](https://ci.appveyor.com/api/projects/status/r6sv51qx36sis1je?branch=develop&svg=true)](https://ci.appveyor.com/project/TomPallister/ocelot-fcfpb) Windows (AppVeyor)
[![Build Status](https://travis-ci.org/ThreeMammals/Ocelot.svg?branch=develop)](https://travis-ci.org/ThreeMammals/Ocelot) Linux & OSX (Travis)
[![Windows Build history](https://buildstats.info/appveyor/chart/TomPallister/ocelot-fcfpb?branch=develop&includeBuildsFromPullRequest=false)](https://ci.appveyor.com/project/TomPallister/ocelot-fcfpb/history?branch=develop)
[![Coverage Status](https://coveralls.io/repos/github/ThreeMammals/Ocelot/badge.svg?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!
[![Support via PayPal](https://cdn.rawgit.com/twolfson/paypal-github-button/1.0.0/dist/button.svg)](https://www.paypal.me/ThreeMammals/)
## Things that are currently annoying me
[![](https://codescene.io/projects/697/status.svg) Get more details at **codescene.io**.](https://codescene.io/projects/697/jobs/latest-successful/results)
[<img src="https://threemammals.com/ocelot_logo.png">](https://threemammals.com/ocelot)
[![CircleCI](https://circleci.com/gh/ThreeMammals/Ocelot/tree/master.svg?style=svg)](https://circleci.com/gh/ThreeMammals/Ocelot/tree/master)
[![Coverage Status](https://coveralls.io/repos/github/ThreeMammals/Ocelot/badge.svg?branch=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!
[![Support via PayPal](https://cdn.rawgit.com/twolfson/paypal-github-button/1.0.0/dist/button.svg)](https://www.paypal.me/ThreeMammals/)
## Things that are currently annoying me
[![](https://codescene.io/projects/697/status.svg) Get more details at **codescene.io**.](https://codescene.io/projects/697/jobs/latest-successful/results)

View File

@ -1 +0,0 @@
image: Visual Studio 2019

View File

@ -1,2 +0,0 @@
./build.ps1 -target BuildAndReleaseUnstable
exit $LASTEXITCODE

View File

@ -1,2 +0,0 @@
./build.ps1 -target RunTests
exit $LASTEXITCODE

View File

@ -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<string>("assets_url");
Information("Assets url " + assets_url);
var assets = GetResource(assets_url);
Information("Assets " + assets_url);
foreach(var asset in Newtonsoft.Json.JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JArray>(assets))
{
Information("In the loop..");
var file = packagesDir + File(asset.Value<string>("name"));
Information("Downloading " + file);
DownloadFile(asset.Value<string>("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<Newtonsoft.Json.Linq.JObject>(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"));
}

View File

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

9
docker/Dockerfile.base Normal file
View File

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

15
docker/Dockerfile.build Normal file
View File

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

20
docker/Dockerfile.release Normal file
View File

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

3
docker/README.md Normal file
View File

@ -0,0 +1,3 @@
# docker build
This folder contains the dockerfile and script to create the ocelot build container.

6
docker/docker-build.sh Executable file
View File

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

View File

@ -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 <http://semver.org/>`_ 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.
* Alternatively you can build the project in VS2019 with the latest .NET Core SDK.

View File

@ -1,4 +1,4 @@
Overview
========
This document summarises the build and release process for the project. The build scripts are written using `Cake <http://cakebuild.net/>`_, 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 <https://www.appveyor.com/>`_), with minimal logic defined in the build server itself.
This document summarises the build and release process for the project. The build scripts are written using `Cake <http://cakebuild.net/>`_, 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 <https://circleci.com//>`_), with minimal logic defined in the build server itself.

View File

@ -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 <https://semver.org/>`_ support for this is provided by `GitVersion <https://gitversion.readthedocs.io/en/latest/>`_. 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 <https://github.com/ThreeMammals/Ocelot/releases/tag/13.0.0>`_ and this in `NuGet <https://www.nuget.org/packages/Ocelot/13.0.0>`_.
Notes
-----
All NuGet package builds are done with AppVeyor `here <https://ci.appveyor.com/project/TomPallister/ocelot-fcfpb>_` and all releases are done from `here <https://ci.appveyor.com/project/TomPallister/ocelot-ayj4w>_`. We also build Ocelot on Travis `here <https://travis-ci.org/ThreeMammals/Ocelot>`_ 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 <https://semver.org/>`_ support for this is provided by `GitVersion <https://gitversion.readthedocs.io/en/latest/>`_. 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 <https://github.com/ThreeMammals/Ocelot/releases/tag/13.0.0>`_ and this in `NuGet <https://www.nuget.org/packages/Ocelot/13.0.0>`_.
Notes
-----
All NuGet package builds & releases are done with CircleCI `here <https://circleci.com/gh/ThreeMammals>_` and all releases are done from `here <https://ci.appveyor.com/project/TomPallister/ocelot-ayj4w>_`.
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.

View File

@ -1,54 +1,54 @@
Caching
=======
Ocelot supports some very rudimentary caching at the moment provider by
the `CacheManager <https://github.com/MichaCo/CacheManager>`_ project. This is an amazing project
that is solving a lot of caching problems. I would reccomend using this package to
cache with Ocelot.
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 <https://github.com/ThreeMammals/Ocelot/blob/develop/test/Ocelot.ManualTest/Program.cs>`_ 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<IOcelotCache<CachedResponse>, MyCache>()``
``IOcelotCache<CachedResponse>`` this is for output caching.
``IOcelotCache<FileConfiguration>`` 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 <https://github.com/MichaCo/CacheManager>`_ project. This is an amazing project
that is solving a lot of caching problems. I would reccomend using this package to
cache with Ocelot.
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 <https://github.com/ThreeMammals/Ocelot/blob/master/test/Ocelot.ManualTest/Program.cs>`_ 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<IOcelotCache<CachedResponse>, MyCache>()``
``IOcelotCache<CachedResponse>`` this is for output caching.
``IOcelotCache<FileConfiguration>`` 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..

View File

@ -1,224 +1,230 @@
Configuration
============
An example configuration can be found `here <https://github.com/ThreeMammals/Ocelot/blob/develop/test/Ocelot.ManualTest/ocelot.json>`_.
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 <https://docs.microsoft.com/en-us/aspnet/core/fundamentals/environments>`_.
Merging configuration files
^^^^^^^^^^^^^^^^^^^^^^^^^^^
This feature was requested in `Issue 296 <https://github.com/ThreeMammals/Ocelot/issues/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 <https://github.com/ThreeMammals/Ocelot/issues/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 <https://github.com/ThreeMammals/Ocelot/issues/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 <https://github.com/ThreeMammals/Ocelot/blob/master/test/Ocelot.ManualTest/ocelot.json>`_.
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 <https://docs.microsoft.com/en-us/aspnet/core/fundamentals/environments>`_.
Merging configuration files
^^^^^^^^^^^^^^^^^^^^^^^^^^^
This feature was requested in `Issue 296 <https://github.com/ThreeMammals/Ocelot/issues/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 <https://github.com/ThreeMammals/Ocelot/issues/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 <https://github.com/ThreeMammals/Ocelot/issues/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.

View File

@ -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 <https://github.com/graphql-dotnet/graphql-dotnet>`_ library.
Please see the sample project `OcelotGraphQL <https://github.com/ThreeMammals/Ocelot/tree/develop/samples/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 <https://github.com/graphql-dotnet/graphql-dotnet>`_ library.
Please see the sample project `OcelotGraphQL <https://github.com/ThreeMammals/Ocelot/tree/master/samples/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 :>

View File

@ -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
^^^^^^^^^^^^^^^

View File

@ -4,7 +4,7 @@ Getting Started
Ocelot is designed to work with .NET Core only and is currently
built to netstandard2.0. `This <https://docs.microsoft.com/en-us/dotnet/articles/standard/library>`_ 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.

View File

@ -1,2 +0,0 @@
./build.ps1 -target Release
exit $LASTEXITCODE

View File

@ -1,2 +0,0 @@
./build -target RunAcceptanceTests
exit $LASTEXITCODE

View File

@ -1,3 +0,0 @@
#!/usr/bin/env bash
./build.sh --target RunAcceptanceTests

View File

@ -1,2 +0,0 @@
./build.ps1 -target RunBenchmarkTests
exit $LASTEXITCODE

View File

@ -1,2 +0,0 @@
./build.ps1 -target RunUnitTests
exit $LASTEXITCODE

View File

@ -1,3 +0,0 @@
#!/usr/bin/env bash
./build.sh --target RunUnitTests

View File

@ -1,13 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Folder Include="wwwroot\"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App"/>
<PackageReference Include="Ocelot" Version="12.0.1"/>
<PackageReference Include="Ocelot.Administration" Version="0.1.0"/>
</ItemGroup>
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Folder Include="wwwroot\"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App"/>
<PackageReference Include="Ocelot" Version="12.0.1"/>
<PackageReference Include="Ocelot.Administration" Version="0.1.0"/>
</ItemGroup>
</Project>

View File

@ -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" ]

View File

@ -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"]

0
samples/Docker/README.md Normal file
View File

View File

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ocelot" Version="13.8.4" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
</Project>

View File

@ -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();
}
}
}

View File

@ -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"
}
}
}

View File

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

View File

@ -0,0 +1,10 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}

View File

@ -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"
}
}

View File

@ -1,24 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Folder Include="wwwroot\" />
</ItemGroup>
<ItemGroup>
<None Update="ocelot.json;appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.2.5" />
<PackageReference Include="Ocelot" Version="13.5.1" />
<PackageReference Include="Ocelot.Provider.Eureka" Version="13.5.1" />
<PackageReference Include="Ocelot.Provider.Polly" Version="13.5.1" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Folder Include="wwwroot\" />
</ItemGroup>
<ItemGroup>
<None Update="ocelot.json;appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Ocelot" Version="13.5.1" />
<PackageReference Include="Ocelot.Provider.Eureka" Version="13.5.1" />
<PackageReference Include="Ocelot.Provider.Polly" Version="13.5.1" />
</ItemGroup>
</Project>

View File

@ -1,20 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Folder Include="wwwroot\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.2.5" />
<PackageReference Include="Steeltoe.Discovery.Client" Version="1.1.0" />
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.3" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Folder Include="wwwroot\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Steeltoe.Discovery.Client" Version="1.1.0" />
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.3" />
</ItemGroup>
</Project>

View File

@ -1,18 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<None Update="ocelot.json;appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Folder Include="wwwroot\"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.9"/>
<PackageReference Include="Ocelot" Version="5.5.1"/>
<PackageReference Include="GraphQL" Version="2.0.0-alpha-870"/>
</ItemGroup>
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<None Update="ocelot.json;appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Folder Include="wwwroot\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Ocelot" Version="5.5.1" />
<PackageReference Include="GraphQL" Version="2.0.0-alpha-870" />
</ItemGroup>
</Project>

View File

@ -1,17 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.7.9" />
<PackageReference Include="Ocelot" Version="13.5.1" />
<PackageReference Include="Ocelot.Provider.Kubernetes" Version="13.5.1" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.7.9" />
<PackageReference Include="Ocelot" Version="13.5.1" />
<PackageReference Include="Ocelot.Provider.Kubernetes" Version="13.5.1" />
</ItemGroup>
</Project>

View File

@ -1,15 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.7.9" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.7.9" />
</ItemGroup>
</Project>

View File

@ -1,22 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<Description>Stateless Web Service for Stateful OcelotApplicationApiGateway App</Description>
<Authors> </Authors>
<TargetFramework>netcoreapp2.0</TargetFramework>
<AssemblyName>OcelotApplicationApiGateway</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>OcelotApplicationApiGateway</PackageId>
</PropertyGroup>
<ItemGroup>
<None Update="ocelot.json;appsettings.json;">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.ServiceFabric" Version="6.1.456" />
<PackageReference Include="Microsoft.ServiceFabric.Services" Version="3.0.456" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Ocelot\Ocelot.csproj" />
</ItemGroup>
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<Description>Stateless Web Service for Stateful OcelotApplicationApiGateway App</Description>
<Authors> </Authors>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AssemblyName>OcelotApplicationApiGateway</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>OcelotApplicationApiGateway</PackageId>
</PropertyGroup>
<ItemGroup>
<None Update="ocelot.json;appsettings.json;">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.ServiceFabric" Version="6.1.456" />
<PackageReference Include="Microsoft.ServiceFabric.Services" Version="3.0.456" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Ocelot\Ocelot.csproj" />
</ItemGroup>
</Project>

View File

@ -1,21 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Stateless Service Application</Description>
<Authors> </Authors>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<AssemblyName>OcelotApplicationService</AssemblyName>
<PackageId>OcelotApplicationService</PackageId>
<PackageTargetFallback>$(PackageTargetFallback)</PackageTargetFallback>
</PropertyGroup>
<ItemGroup>&#xD;
&#xD;
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.ServiceFabric" Version="6.1.456"/>
<PackageReference Include="Microsoft.ServiceFabric.Services" Version="3.0.456"/>
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.1"/>
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.2"/>
<PackageReference Include="Microsoft.ServiceFabric.AspNetCore.Kestrel" Version="3.0.456"/>
</ItemGroup>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Stateless Service Application</Description>
<Authors> </Authors>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AssemblyName>OcelotApplicationService</AssemblyName>
<PackageId>OcelotApplicationService</PackageId>
<PackageTargetFallback>$(PackageTargetFallback)</PackageTargetFallback>
</PropertyGroup>
<ItemGroup>&#xD;
&#xD;
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.ServiceFabric" Version="6.1.456"/>
<PackageReference Include="Microsoft.ServiceFabric.Services" Version="3.0.456"/>
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.1"/>
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.2"/>
<PackageReference Include="Microsoft.ServiceFabric.AspNetCore.Kestrel" Version="3.0.456"/>
</ItemGroup>
</Project>

View File

@ -1,39 +1,39 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<NoPackageAnalysis>true</NoPackageAnalysis>
<Description>Provides Ocelot extensions to use the administration API and IdentityService dependencies that come with it</Description>
<AssemblyTitle>Ocelot.Administration</AssemblyTitle>
<VersionPrefix>0.0.0-dev</VersionPrefix>
<AssemblyName>Ocelot.Administration</AssemblyName>
<PackageId>Ocelot.Administration</PackageId>
<PackageTags>API Gateway;.NET core</PackageTags>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Administration</PackageProjectUrl>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Administration</PackageProjectUrl>
<PackageIconUrl>http://threemammals.com/images/ocelot_logo.png</PackageIconUrl>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<Authors>Tom Pallister</Authors>
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.66">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
<PackageReference Include="IdentityServer4" Version="3.0.1" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-19367-01" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<NoPackageAnalysis>true</NoPackageAnalysis>
<Description>Provides Ocelot extensions to use the administration API and IdentityService dependencies that come with it</Description>
<AssemblyTitle>Ocelot.Administration</AssemblyTitle>
<VersionPrefix>0.0.0-dev</VersionPrefix>
<AssemblyName>Ocelot.Administration</AssemblyName>
<PackageId>Ocelot.Administration</PackageId>
<PackageTags>API Gateway;.NET core</PackageTags>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Administration</PackageProjectUrl>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Administration</PackageProjectUrl>
<PackageIconUrl>http://threemammals.com/images/ocelot_logo.png</PackageIconUrl>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<Authors>Tom Pallister</Authors>
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.66">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
<PackageReference Include="IdentityServer4" Version="3.0.1" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
</ItemGroup>
</Project>

View File

@ -1,39 +1,39 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<NoPackageAnalysis>true</NoPackageAnalysis>
<Description>Provides Ocelot extensions to use CacheManager.Net</Description>
<AssemblyTitle>Ocelot.Cache.CacheManager</AssemblyTitle>
<VersionPrefix>0.0.0-dev</VersionPrefix>
<AssemblyName>Ocelot.Cache.CacheManager</AssemblyName>
<PackageId>Ocelot.Cache.CacheManager</PackageId>
<PackageTags>API Gateway;.NET core</PackageTags>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Cache.CacheManager</PackageProjectUrl>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Cache.CacheManager</PackageProjectUrl>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<Authors>Tom Pallister</Authors>
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.66">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="CacheManager.Core" Version="2.0.0-beta-1629" />
<PackageReference Include="CacheManager.Microsoft.Extensions.Configuration" Version="2.0.0-beta-1629" />
<PackageReference Include="CacheManager.Microsoft.Extensions.Logging" Version="2.0.0-beta-1629" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-19367-01" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<NoPackageAnalysis>true</NoPackageAnalysis>
<Description>Provides Ocelot extensions to use CacheManager.Net</Description>
<AssemblyTitle>Ocelot.Cache.CacheManager</AssemblyTitle>
<VersionPrefix>0.0.0-dev</VersionPrefix>
<AssemblyName>Ocelot.Cache.CacheManager</AssemblyName>
<PackageId>Ocelot.Cache.CacheManager</PackageId>
<PackageTags>API Gateway;.NET core</PackageTags>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Cache.CacheManager</PackageProjectUrl>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Cache.CacheManager</PackageProjectUrl>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<Authors>Tom Pallister</Authors>
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.66">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="CacheManager.Core" Version="2.0.0-beta-1629" />
<PackageReference Include="CacheManager.Microsoft.Extensions.Configuration" Version="2.0.0-beta-1629" />
<PackageReference Include="CacheManager.Microsoft.Extensions.Logging" Version="2.0.0-beta-1629" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
</ItemGroup>
</Project>

View File

@ -1,38 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<NoPackageAnalysis>true</NoPackageAnalysis>
<Description>Provides Ocelot extensions to use Consul</Description>
<AssemblyTitle>Ocelot.Provider.Consul</AssemblyTitle>
<VersionPrefix>0.0.0-dev</VersionPrefix>
<AssemblyName>Ocelot.Provider.Consul</AssemblyName>
<PackageId>Ocelot.Provider.Consul</PackageId>
<PackageTags>API Gateway;.NET core</PackageTags>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Consul</PackageProjectUrl>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Consul</PackageProjectUrl>
<PackageIconUrl>http://threemammals.com/images/ocelot_logo.png</PackageIconUrl>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<Authors>Tom Pallister</Authors>
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Consul" Version="0.7.2.6" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.66">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-19367-01" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<NoPackageAnalysis>true</NoPackageAnalysis>
<Description>Provides Ocelot extensions to use Consul</Description>
<AssemblyTitle>Ocelot.Provider.Consul</AssemblyTitle>
<VersionPrefix>0.0.0-dev</VersionPrefix>
<AssemblyName>Ocelot.Provider.Consul</AssemblyName>
<PackageId>Ocelot.Provider.Consul</PackageId>
<PackageTags>API Gateway;.NET core</PackageTags>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Consul</PackageProjectUrl>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Consul</PackageProjectUrl>
<PackageIconUrl>http://threemammals.com/images/ocelot_logo.png</PackageIconUrl>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<Authors>Tom Pallister</Authors>
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Consul" Version="0.7.2.6" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.66">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
</ItemGroup>
</Project>

View File

@ -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<string>())));
services.AddRange(instances.Select(i => new Service(i.ServiceId, new ServiceHostAndPort(i.Host, i.Port, i.Uri.Scheme), "", "", new List<string>())));
}
return Task.FromResult(services);

View File

@ -1,38 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<NoPackageAnalysis>true</NoPackageAnalysis>
<Description>Provides Ocelot extensions to use Eureka</Description>
<AssemblyTitle>Ocelot.Provider.Eureka</AssemblyTitle>
<VersionPrefix>0.0.0-dev</VersionPrefix>
<AssemblyName>Ocelot.Provider.Eureka</AssemblyName>
<PackageId>Ocelot.Provider.Eureka</PackageId>
<PackageTags>API Gateway;.NET core</PackageTags>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Eureka</PackageProjectUrl>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Eureka</PackageProjectUrl>
<PackageIconUrl>http://threemammals.com/images/ocelot_logo.png</PackageIconUrl>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<Authors>Tom Pallister</Authors>
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Steeltoe.Discovery.ClientCore" Version="2.3.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.66">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-19367-01" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<NoPackageAnalysis>true</NoPackageAnalysis>
<Description>Provides Ocelot extensions to use Eureka</Description>
<AssemblyTitle>Ocelot.Provider.Eureka</AssemblyTitle>
<VersionPrefix>0.0.0-dev</VersionPrefix>
<AssemblyName>Ocelot.Provider.Eureka</AssemblyName>
<PackageId>Ocelot.Provider.Eureka</PackageId>
<PackageTags>API Gateway;.NET core</PackageTags>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Eureka</PackageProjectUrl>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Eureka</PackageProjectUrl>
<PackageIconUrl>http://threemammals.com/images/ocelot_logo.png</PackageIconUrl>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<Authors>Tom Pallister</Authors>
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Steeltoe.Discovery.ClientCore" Version="2.3.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.66">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
</ItemGroup>
</Project>

View File

@ -1,43 +1,43 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<NoPackageAnalysis>true</NoPackageAnalysis>
<Product>Ocelot</Product>
<Description>Provides Ocelot extensions to use kubernetes</Description>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot</PackageProjectUrl>
<PackageIconUrl>http://threemammals.com/images/ocelot_logo.png</PackageIconUrl>
<PackageReleaseNotes></PackageReleaseNotes>
<AssemblyName>Ocelot.Provider.Kubernetes</AssemblyName>
<PackageId>Ocelot.Provider.Kubernetes</PackageId>
<PackageTags>API Gateway;.NET core</PackageTags>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<Version>0.0.0-dev</Version>
<Authors>geffzhang</Authors>
<Company />
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Compile Remove="IKubeApiClientFactory.cs" />
<Compile Remove="KubeApiClientFactory.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="KubeClient" Version="2.3.4" />
<PackageReference Include="KubeClient.Extensions.DependencyInjection" Version="2.3.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-19367-01" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<NoPackageAnalysis>true</NoPackageAnalysis>
<Product>Ocelot</Product>
<Description>Provides Ocelot extensions to use kubernetes</Description>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot</PackageProjectUrl>
<PackageIconUrl>http://threemammals.com/images/ocelot_logo.png</PackageIconUrl>
<PackageReleaseNotes></PackageReleaseNotes>
<AssemblyName>Ocelot.Provider.Kubernetes</AssemblyName>
<PackageId>Ocelot.Provider.Kubernetes</PackageId>
<PackageTags>API Gateway;.NET core</PackageTags>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<Version>0.0.0-dev</Version>
<Authors>geffzhang</Authors>
<Company />
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Compile Remove="IKubeApiClientFactory.cs" />
<Compile Remove="KubeApiClientFactory.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="KubeClient" Version="2.3.4" />
<PackageReference Include="KubeClient.Extensions.DependencyInjection" Version="2.3.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
</ItemGroup>
</Project>

View File

@ -1,38 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<NoPackageAnalysis>true</NoPackageAnalysis>
<Description>Provides Ocelot extensions to use Polly.NET</Description>
<AssemblyTitle>Ocelot.Provider.Polly</AssemblyTitle>
<VersionPrefix>0.0.0-dev</VersionPrefix>
<AssemblyName>Ocelot.Provider.Polly</AssemblyName>
<PackageId>Ocelot.Provider.Polly</PackageId>
<PackageTags>API Gateway;.NET core</PackageTags>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Polly</PackageProjectUrl>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Polly</PackageProjectUrl>
<PackageIconUrl>http://threemammals.com/images/ocelot_logo.png</PackageIconUrl>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<Authors>Tom Pallister</Authors>
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.66">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Polly" Version="7.1.1" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-19367-01" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<NoPackageAnalysis>true</NoPackageAnalysis>
<Description>Provides Ocelot extensions to use Polly.NET</Description>
<AssemblyTitle>Ocelot.Provider.Polly</AssemblyTitle>
<VersionPrefix>0.0.0-dev</VersionPrefix>
<AssemblyName>Ocelot.Provider.Polly</AssemblyName>
<PackageId>Ocelot.Provider.Polly</PackageId>
<PackageTags>API Gateway;.NET core</PackageTags>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Polly</PackageProjectUrl>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Polly</PackageProjectUrl>
<PackageIconUrl>http://threemammals.com/images/ocelot_logo.png</PackageIconUrl>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<Authors>Tom Pallister</Authors>
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.66">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Polly" Version="7.1.1" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
</ItemGroup>
</Project>

View File

@ -1,40 +1,40 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<NoPackageAnalysis>true</NoPackageAnalysis>
<Description>Provides Ocelot extensions to use Rafty</Description>
<AssemblyTitle>Ocelot.Provider.Rafty</AssemblyTitle>
<VersionPrefix>0.0.0-dev</VersionPrefix>
<AssemblyName>Ocelot.Provider.Rafty</AssemblyName>
<PackageId>Ocelot.Provider.Rafty</PackageId>
<PackageTags>API Gateway;.NET core</PackageTags>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Rafty</PackageProjectUrl>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Rafty</PackageProjectUrl>
<PackageIconUrl>http://threemammals.com/images/ocelot_logo.png</PackageIconUrl>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<Authors>Tom Pallister</Authors>
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
<ProjectReference Include="..\Ocelot.Administration\Ocelot.Administration.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.SQLite" Version="3.0.0" />
<PackageReference Include="Rafty" Version="0.4.4" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.66">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-19367-01" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<NoPackageAnalysis>true</NoPackageAnalysis>
<Description>Provides Ocelot extensions to use Rafty</Description>
<AssemblyTitle>Ocelot.Provider.Rafty</AssemblyTitle>
<VersionPrefix>0.0.0-dev</VersionPrefix>
<AssemblyName>Ocelot.Provider.Rafty</AssemblyName>
<PackageId>Ocelot.Provider.Rafty</PackageId>
<PackageTags>API Gateway;.NET core</PackageTags>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Rafty</PackageProjectUrl>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Rafty</PackageProjectUrl>
<PackageIconUrl>http://threemammals.com/images/ocelot_logo.png</PackageIconUrl>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<Authors>Tom Pallister</Authors>
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
<ProjectReference Include="..\Ocelot.Administration\Ocelot.Administration.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.SQLite" Version="3.0.0" />
<PackageReference Include="Rafty" Version="0.4.4" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.66">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
</ItemGroup>
</Project>

View File

@ -1,38 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<NoPackageAnalysis>true</NoPackageAnalysis>
<Description>This package provides methods to integrate Butterfly tracing with Ocelot.</Description>
<AssemblyTitle>Ocelot.Tracing.Butterfly</AssemblyTitle>
<VersionPrefix>0.0.0-dev</VersionPrefix>
<AssemblyName>Ocelot.Tracing.Butterfly</AssemblyName>
<PackageId>Ocelot.Tracing.Butterfly</PackageId>
<PackageTags>API Gateway;.NET core; Butterfly; ButterflyAPM</PackageTags>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot</PackageProjectUrl>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot</PackageProjectUrl>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<Authors>Tom Pallister</Authors>
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
<RootNamespace>Ocelot.Tracing.Butterfly</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Butterfly.Client" Version="0.0.8" />
<PackageReference Include="Butterfly.Client.AspNetCore" Version="0.0.8" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-19367-01" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<NoPackageAnalysis>true</NoPackageAnalysis>
<Description>This package provides methods to integrate Butterfly tracing with Ocelot.</Description>
<AssemblyTitle>Ocelot.Tracing.Butterfly</AssemblyTitle>
<VersionPrefix>0.0.0-dev</VersionPrefix>
<AssemblyName>Ocelot.Tracing.Butterfly</AssemblyName>
<PackageId>Ocelot.Tracing.Butterfly</PackageId>
<PackageTags>API Gateway;.NET core; Butterfly; ButterflyAPM</PackageTags>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot</PackageProjectUrl>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot</PackageProjectUrl>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<Authors>Tom Pallister</Authors>
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
<RootNamespace>Ocelot.Tracing.Butterfly</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Butterfly.Client" Version="0.0.8" />
<PackageReference Include="Butterfly.Client.AspNetCore" Version="0.0.8" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
</ItemGroup>
</Project>

View File

@ -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<string> _clientWhitelist;
private Func<List<string>> _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<string> clientWhitelist)
public RateLimitOptionsBuilder WithClientWhiteList(Func<List<string>> 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);
}
}
}
}

View File

@ -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);
}
}
}

View File

@ -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)

View File

@ -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; }
}
}

View File

@ -6,32 +6,44 @@
/// </summary>
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;
}
/// <summary>
/// Specify if auto redirect is enabled
/// </summary>
public bool AllowAutoRedirect { get; private set; }
/// </summary>
/// <value>AllowAutoRedirect</value>
public bool AllowAutoRedirect { get; private set; }
/// <summary>
/// Specify is handler has to use a cookie container
/// </summary>
/// </summary>
/// <value>UseCookieContainer</value>
public bool UseCookieContainer { get; private set; }
/// <summary>
/// Specify is handler has to use a opentracing
/// </summary>
/// </summary>
/// <value>UseTracing</value>
public bool UseTracing { get; private set; }
/// <summary>
/// Specify if handler has to use a proxy
/// </summary>
public bool UseProxy { get; private set; }
/// </summary>
/// <value>UseProxy</value>
public bool UseProxy { get; private set; }
/// <summary>
/// Specify the maximum of concurrent connection to a network endpoint
/// </summary>
/// <value>MaxConnectionsPerServer</value>
public int MaxConnectionsPerServer { get; private set; }
}
}

View File

@ -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);
}
}
}

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
namespace Ocelot.Configuration
{
@ -7,12 +8,14 @@ namespace Ocelot.Configuration
/// </summary>
public class RateLimitOptions
{
public RateLimitOptions(bool enbleRateLimiting, string clientIdHeader, List<string> clientWhitelist, bool disableRateLimitHeaders,
private readonly Func<List<string>> _getClientWhitelist;
public RateLimitOptions(bool enableRateLimiting, string clientIdHeader, Func<List<string>> getClientWhitelist, bool disableRateLimitHeaders,
string quotaExceededMessage, string rateLimitCounterPrefix, RateLimitRule rateLimitRule, int httpStatusCode)
{
EnableRateLimiting = enbleRateLimiting;
EnableRateLimiting = enableRateLimiting;
ClientIdHeader = clientIdHeader;
ClientWhitelist = clientWhitelist ?? new List<string>();
_getClientWhitelist = getClientWhitelist;
DisableRateLimitHeaders = disableRateLimitHeaders;
QuotaExceededMessage = quotaExceededMessage;
RateLimitCounterPrefix = rateLimitCounterPrefix;
@ -22,18 +25,21 @@ namespace Ocelot.Configuration
public RateLimitRule RateLimitRule { get; private set; }
public List<string> ClientWhitelist { get; private set; }
/// <summary>
/// Gets the list of white listed clients
/// </summary>
public List<string> ClientWhitelist { get => _getClientWhitelist(); }
/// <summary>
/// Gets or sets the HTTP header that holds the client identifier, by default is X-ClientId
/// </summary>
public string ClientIdHeader { get; private set; }
public string ClientIdHeader { get; private set; }
/// <summary>
/// Gets or sets the HTTP Status code returned when rate limiting occurs, by default value is set to 429 (Too Many Requests)
/// </summary>
public int HttpStatusCode { get; private set; }
public int HttpStatusCode { get; private set; }
/// <summary>
/// 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
/// <summary>
/// Gets or sets the counter prefix, used to compose the rate limit counter cache key
/// </summary>
public string RateLimitCounterPrefix { get; private set; }
public string RateLimitCounterPrefix { get; private set; }
/// <summary>
/// Enables endpoint rate limiting based URL path and HTTP verb
/// </summary>
@ -56,4 +62,4 @@ namespace Ocelot.Configuration
/// </summary>
public bool DisableRateLimitHeaders { get; private set; }
}
}
}

View File

@ -105,7 +105,8 @@ namespace Ocelot.Configuration.Repository
public void Dispose()
{
_timer.Dispose();
_timer?.Dispose();
_timer = null;
}
}
}

View File

@ -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<FileConfiguration>, 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<FileReRoute> reRoutes)
{

View File

@ -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))
{

View File

@ -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);

View File

@ -1,42 +1,42 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<NoPackageAnalysis>true</NoPackageAnalysis>
<Description>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.</Description>
<AssemblyTitle>Ocelot</AssemblyTitle>
<VersionPrefix>0.0.0-dev</VersionPrefix>
<AssemblyName>Ocelot</AssemblyName>
<PackageId>Ocelot</PackageId>
<PackageTags>API Gateway;.NET core</PackageTags>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot</PackageProjectUrl>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot</PackageProjectUrl>
<PackageIconUrl>http://threemammals.com/images/ocelot_logo.png</PackageIconUrl>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<Authors>Tom Pallister</Authors>
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentValidation" Version="8.5.0" />
<PackageReference Include="Microsoft.AspNetCore.MiddlewareAnalysis" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.DiagnosticAdapter" Version="3.0.0">
<NoWarn>NU1701</NoWarn>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.0.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.66">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-19367-01" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<NoPackageAnalysis>true</NoPackageAnalysis>
<Description>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.</Description>
<AssemblyTitle>Ocelot</AssemblyTitle>
<VersionPrefix>0.0.0-dev</VersionPrefix>
<AssemblyName>Ocelot</AssemblyName>
<PackageId>Ocelot</PackageId>
<PackageTags>API Gateway;.NET core</PackageTags>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot</PackageProjectUrl>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot</PackageProjectUrl>
<PackageIconUrl>http://threemammals.com/images/ocelot_logo.png</PackageIconUrl>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<Authors>Tom Pallister</Authors>
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentValidation" Version="8.5.0" />
<PackageReference Include="Microsoft.AspNetCore.MiddlewareAnalysis" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.DiagnosticAdapter" Version="3.0.0">
<NoWarn>NU1701</NoWarn>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.0.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.66">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
</ItemGroup>
</Project>

View File

@ -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()
};
}

View File

@ -1,6 +1,6 @@
namespace Ocelot.Requester
{
using Errors;
using Ocelot.Errors;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
@ -23,7 +23,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);
}

View File

@ -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<HttpResponseMessage> 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}");
}
}
}
}

View File

@ -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);
}

View File

@ -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; }
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51899,
}
},
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "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<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 52839,
}
},
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "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<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51899,
}
},
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "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<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51899,
}
},
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "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<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 57899,
}
},
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "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<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 52839,
}
},
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "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<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 57879,
}
},
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "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<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 57873,
}
},
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "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();
}
}
}

View File

@ -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))

View File

@ -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<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51871,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
UpstreamHeaderTransform = new Dictionary<string,string>
{
{"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<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51871,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
DownstreamHeaderTransform = new Dictionary<string,string>
{
{"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<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 6773,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
DownstreamHeaderTransform = new Dictionary<string,string>
{
{"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<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 6773,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
DownstreamHeaderTransform = new Dictionary<string,string>
{
{"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<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 6773,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
DownstreamHeaderTransform = new Dictionary<string,string>
{
{"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<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/sso/{everything}",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 6774,
}
},
UpstreamPathTemplate = "/sso/{everything}",
UpstreamHttpMethod = new List<string> { "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<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/sso/{everything}",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 6775,
}
},
UpstreamPathTemplate = "/sso/{everything}",
UpstreamHttpMethod = new List<string> { "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<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "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<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "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<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51871,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
UpstreamHeaderTransform = new Dictionary<string,string>
{
{"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<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51871,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
DownstreamHeaderTransform = new Dictionary<string,string>
{
{"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<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 6773,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
DownstreamHeaderTransform = new Dictionary<string,string>
{
{"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<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 6773,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
DownstreamHeaderTransform = new Dictionary<string,string>
{
{"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<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 6773,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
DownstreamHeaderTransform = new Dictionary<string,string>
{
{"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<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/sso/{everything}",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 6774,
}
},
UpstreamPathTemplate = "/sso/{everything}",
UpstreamHttpMethod = new List<string> { "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<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/sso/{everything}",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 6775,
}
},
UpstreamPathTemplate = "/sso/{everything}",
UpstreamHttpMethod = new List<string> { "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<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 52866,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:52866", "/", 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<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51874,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51874", "/", 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();
}
}
}

View File

@ -1,163 +1,163 @@
namespace Ocelot.AcceptanceTests
{
using Configuration;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Requester;
using Shouldly;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net;
using TestStack.BDDfy;
using Xunit;
public class HttpClientCachingTests : IDisposable
{
private readonly Steps _steps;
private string _downstreamPath;
private readonly ServiceHandler _serviceHandler;
public HttpClientCachingTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
}
[Fact]
public void should_cache_one_http_client_same_re_route()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
}
}
};
var cache = new FakeHttpClientCache();
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningWithFakeHttpClientCache(cache))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.And(x => cache.Count.ShouldBe(1))
.BDDfy();
}
[Fact]
public void should_cache_two_http_client_different_re_route()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
},
new FileReRoute
{
DownstreamPathTemplate = "/two",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/two",
UpstreamHttpMethod = new List<string> { "Get" },
}
}
};
var cache = new FakeHttpClientCache();
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningWithFakeHttpClientCache(cache))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/two"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/two"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/two"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.And(x => cache.Count.ShouldBe(2))
.BDDfy();
}
private void GivenThereIsAServiceRunningOn(string baseUrl, int statusCode, string responseBody)
{
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, async context =>
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
});
}
public void Dispose()
{
_serviceHandler.Dispose();
_steps.Dispose();
}
public class FakeHttpClientCache : IHttpClientCache
{
private readonly ConcurrentDictionary<DownstreamReRoute, IHttpClient> _httpClientsCache;
public FakeHttpClientCache()
{
_httpClientsCache = new ConcurrentDictionary<DownstreamReRoute, IHttpClient>();
}
public void Set(DownstreamReRoute key, IHttpClient client, TimeSpan expirationTime)
{
_httpClientsCache.AddOrUpdate(key, client, (k, oldValue) => client);
}
public IHttpClient Get(DownstreamReRoute key)
{
//todo handle error?
return _httpClientsCache.TryGetValue(key, out var client) ? client : null;
}
public int Count => _httpClientsCache.Count;
}
}
}
namespace Ocelot.AcceptanceTests
{
using Configuration;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Requester;
using Shouldly;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net;
using TestStack.BDDfy;
using Xunit;
public class HttpClientCachingTests : IDisposable
{
private readonly Steps _steps;
private string _downstreamPath;
private readonly ServiceHandler _serviceHandler;
public HttpClientCachingTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
}
[Fact]
public void should_cache_one_http_client_same_re_route()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 58814,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
}
}
};
var cache = new FakeHttpClientCache();
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:58814", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningWithFakeHttpClientCache(cache))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.And(x => cache.Count.ShouldBe(1))
.BDDfy();
}
[Fact]
public void should_cache_two_http_client_different_re_route()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 58817,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
},
new FileReRoute
{
DownstreamPathTemplate = "/two",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 58817,
}
},
UpstreamPathTemplate = "/two",
UpstreamHttpMethod = new List<string> { "Get" },
}
}
};
var cache = new FakeHttpClientCache();
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:58817", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningWithFakeHttpClientCache(cache))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/two"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/two"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/two"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.And(x => cache.Count.ShouldBe(2))
.BDDfy();
}
private void GivenThereIsAServiceRunningOn(string baseUrl, int statusCode, string responseBody)
{
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, async context =>
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
});
}
public void Dispose()
{
_serviceHandler.Dispose();
_steps.Dispose();
}
public class FakeHttpClientCache : IHttpClientCache
{
private readonly ConcurrentDictionary<DownstreamReRoute, IHttpClient> _httpClientsCache;
public FakeHttpClientCache()
{
_httpClientsCache = new ConcurrentDictionary<DownstreamReRoute, IHttpClient>();
}
public void Set(DownstreamReRoute key, IHttpClient client, TimeSpan expirationTime)
{
_httpClientsCache.AddOrUpdate(key, client, (k, oldValue) => client);
}
public IHttpClient Get(DownstreamReRoute key)
{
//todo handle error?
return _httpClientsCache.TryGetValue(key, out var client) ? client : null;
}
public int Count => _httpClientsCache.Count;
}
}
}

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<VersionPrefix>0.0.0-dev</VersionPrefix>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AssemblyName>Ocelot.AcceptanceTests</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>Ocelot.AcceptanceTests</PackageId>
@ -40,6 +40,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="3.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
<PackageReference Include="Moq" Version="4.13.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.66">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
@ -69,6 +70,6 @@
<PackageReference Include="Pivotal.Discovery.ClientCore" Version="2.2.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-19367-01" />
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
</ItemGroup>
</Project>

View File

@ -1,274 +1,274 @@
namespace Ocelot.AcceptanceTests
{
using Configuration.File;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using TestStack.BDDfy;
using Xunit;
public class PollyQoSTests : IDisposable
{
private readonly Steps _steps;
private int _requestCount;
private readonly ServiceHandler _serviceHandler;
public PollyQoSTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
}
[Fact]
public void should_not_timeout()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51569,
}
},
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Post" },
QoSOptions = new FileQoSOptions
{
TimeoutValue = 1000,
ExceptionsAllowedBeforeBreaking = 10
}
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51569", 200, string.Empty, 10))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningWithPolly())
.And(x => _steps.GivenThePostHasContent("postContent"))
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.BDDfy();
}
[Fact]
public void should_timeout()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51579,
}
},
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Post" },
QoSOptions = new FileQoSOptions
{
TimeoutValue = 10,
ExceptionsAllowedBeforeBreaking = 10
}
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51579", 201, string.Empty, 1000))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningWithPolly())
.And(x => _steps.GivenThePostHasContent("postContent"))
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
.BDDfy();
}
[Fact]
public void should_open_circuit_breaker_then_close()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51892,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
QoSOptions = new FileQoSOptions
{
ExceptionsAllowedBeforeBreaking = 1,
TimeoutValue = 500,
DurationOfBreak = 1000
},
}
}
};
this.Given(x => x.GivenThereIsAPossiblyBrokenServiceRunningOn("http://localhost:51892", "Hello from Laura"))
.Given(x => _steps.GivenThereIsAConfiguration(configuration))
.Given(x => _steps.GivenOcelotIsRunningWithPolly())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.Given(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Given(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
.Given(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Given(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
.Given(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Given(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
.Given(x => x.GivenIWaitMilliseconds(3000))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy();
}
[Fact]
public void open_circuit_should_not_effect_different_reRoute()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51872,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
QoSOptions = new FileQoSOptions
{
ExceptionsAllowedBeforeBreaking = 1,
TimeoutValue = 500,
DurationOfBreak = 1000
}
},
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51880,
}
},
UpstreamPathTemplate = "/working",
UpstreamHttpMethod = new List<string> { "Get" },
}
}
};
this.Given(x => x.GivenThereIsAPossiblyBrokenServiceRunningOn("http://localhost:51872", "Hello from Laura"))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51880/", 200, "Hello from Tom", 0))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningWithPolly())
.And(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.And(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
.And(x => _steps.WhenIGetUrlOnTheApiGateway("/working"))
.And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Tom"))
.And(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
.And(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
.And(x => x.GivenIWaitMilliseconds(3000))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy();
}
private void GivenIWaitMilliseconds(int ms)
{
Thread.Sleep(ms);
}
private void GivenThereIsAPossiblyBrokenServiceRunningOn(string url, string responseBody)
{
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
{
//circuit starts closed
if (_requestCount == 0)
{
_requestCount++;
context.Response.StatusCode = 200;
await context.Response.WriteAsync(responseBody);
return;
}
//request one times out and polly throws exception, circuit opens
if (_requestCount == 1)
{
_requestCount++;
await Task.Delay(1000);
context.Response.StatusCode = 200;
return;
}
//after break closes we return 200 OK
if (_requestCount == 2)
{
context.Response.StatusCode = 200;
await context.Response.WriteAsync(responseBody);
}
});
}
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody, int timeout)
{
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
{
Thread.Sleep(timeout);
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
});
}
public void Dispose()
{
_serviceHandler?.Dispose();
_steps.Dispose();
}
}
}
namespace Ocelot.AcceptanceTests
{
using Configuration.File;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using TestStack.BDDfy;
using Xunit;
public class PollyQoSTests : IDisposable
{
private readonly Steps _steps;
private int _requestCount;
private readonly ServiceHandler _serviceHandler;
public PollyQoSTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
}
[Fact]
public void should_not_timeout()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51569,
}
},
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Post" },
QoSOptions = new FileQoSOptions
{
TimeoutValue = 1000,
ExceptionsAllowedBeforeBreaking = 10
}
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51569", 200, string.Empty, 10))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningWithPolly())
.And(x => _steps.GivenThePostHasContent("postContent"))
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.BDDfy();
}
[Fact]
public void should_timeout()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51579,
}
},
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Post" },
QoSOptions = new FileQoSOptions
{
TimeoutValue = 10,
ExceptionsAllowedBeforeBreaking = 10
}
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51579", 201, string.Empty, 1000))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningWithPolly())
.And(x => _steps.GivenThePostHasContent("postContent"))
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
.BDDfy();
}
[Fact]
public void should_open_circuit_breaker_then_close()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51892,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
QoSOptions = new FileQoSOptions
{
ExceptionsAllowedBeforeBreaking = 1,
TimeoutValue = 500,
DurationOfBreak = 1000
},
}
}
};
this.Given(x => x.GivenThereIsAPossiblyBrokenServiceRunningOn("http://localhost:51892", "Hello from Laura"))
.Given(x => _steps.GivenThereIsAConfiguration(configuration))
.Given(x => _steps.GivenOcelotIsRunningWithPolly())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.Given(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Given(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
.Given(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Given(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
.Given(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Given(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
.Given(x => x.GivenIWaitMilliseconds(3000))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy();
}
[Fact]
public void open_circuit_should_not_effect_different_reRoute()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51870,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
QoSOptions = new FileQoSOptions
{
ExceptionsAllowedBeforeBreaking = 1,
TimeoutValue = 500,
DurationOfBreak = 1000
}
},
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51880,
}
},
UpstreamPathTemplate = "/working",
UpstreamHttpMethod = new List<string> { "Get" },
}
}
};
this.Given(x => x.GivenThereIsAPossiblyBrokenServiceRunningOn("http://localhost:51870", "Hello from Laura"))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51880/", 200, "Hello from Tom", 0))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningWithPolly())
.And(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.And(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
.And(x => _steps.WhenIGetUrlOnTheApiGateway("/working"))
.And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Tom"))
.And(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
.And(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.ServiceUnavailable))
.And(x => x.GivenIWaitMilliseconds(3000))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy();
}
private void GivenIWaitMilliseconds(int ms)
{
Thread.Sleep(ms);
}
private void GivenThereIsAPossiblyBrokenServiceRunningOn(string url, string responseBody)
{
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
{
//circuit starts closed
if (_requestCount == 0)
{
_requestCount++;
context.Response.StatusCode = 200;
await context.Response.WriteAsync(responseBody);
return;
}
//request one times out and polly throws exception, circuit opens
if (_requestCount == 1)
{
_requestCount++;
await Task.Delay(1000);
context.Response.StatusCode = 200;
return;
}
//after break closes we return 200 OK
if (_requestCount == 2)
{
context.Response.StatusCode = 200;
await context.Response.WriteAsync(responseBody);
}
});
}
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody, int timeout)
{
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
{
Thread.Sleep(timeout);
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
});
}
public void Dispose()
{
_serviceHandler?.Dispose();
_steps.Dispose();
}
}
}

View File

@ -1,68 +1,68 @@
namespace Ocelot.AcceptanceTests
{
using Ocelot.Configuration.File;
using System;
using System.Collections.Generic;
using System.Net;
using TestStack.BDDfy;
using Xunit;
public class ResponseCodeTests : IDisposable
{
private readonly Steps _steps;
private readonly ServiceHandler _serviceHandler;
public ResponseCodeTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
}
[Fact]
public void should_return_response_304_when_service_returns_304()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/{everything}",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/{everything}",
UpstreamHttpMethod = new List<string> { "Get" },
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/inline.132.bundle.js", 304))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/inline.132.bundle.js"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotModified))
.BDDfy();
}
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode)
{
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
{
context.Response.StatusCode = statusCode;
});
}
public void Dispose()
{
_serviceHandler?.Dispose();
_steps.Dispose();
}
}
}
namespace Ocelot.AcceptanceTests
{
using Ocelot.Configuration.File;
using System;
using System.Collections.Generic;
using System.Net;
using TestStack.BDDfy;
using Xunit;
public class ResponseCodeTests : IDisposable
{
private readonly Steps _steps;
private readonly ServiceHandler _serviceHandler;
public ResponseCodeTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
}
[Fact]
public void should_return_response_304_when_service_returns_304()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/{everything}",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51092,
}
},
UpstreamPathTemplate = "/{everything}",
UpstreamHttpMethod = new List<string> { "Get" },
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51092", "/inline.132.bundle.js", 304))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/inline.132.bundle.js"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotModified))
.BDDfy();
}
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode)
{
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
{
context.Response.StatusCode = statusCode;
});
}
public void Dispose()
{
_serviceHandler?.Dispose();
_steps.Dispose();
}
}
}

View File

@ -20,7 +20,8 @@
[Fact]
public void should_return_internal_server_error_if_downstream_service_returns_internal_server_error()
{
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
@ -49,6 +50,39 @@
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.InternalServerError))
.BDDfy();
}
[Fact]
public void should_log_warning_if_downstream_service_returns_internal_server_error()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 53876,
},
},
DownstreamScheme = "http",
},
},
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:53876"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningWithLogger())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenWarningShouldBeLogged())
.BDDfy();
}
private void GivenThereIsAServiceRunningOn(string url)

View File

@ -84,14 +84,14 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
Port = 57873,
}
}
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/", "/", 200, "Hello from Laura"))
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:57873/", "/", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
@ -131,7 +131,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
Port = 50810,
}
},
UpstreamPathTemplate = "/",
@ -180,7 +180,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
Port = 50810,
}
},
UpstreamPathTemplate = "/",
@ -189,7 +189,7 @@ namespace Ocelot.AcceptanceTests
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/", "/", 200, "Hello from Laura"))
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:50810/", "/", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
@ -263,7 +263,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
Port = 51005,
}
},
UpstreamPathTemplate = "/{url}",
@ -272,7 +272,7 @@ namespace Ocelot.AcceptanceTests
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Hello from Laura"))
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51005", "/", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway(""))
@ -297,7 +297,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
Port = 58589,
}
},
UpstreamPathTemplate = "/",
@ -306,7 +306,7 @@ namespace Ocelot.AcceptanceTests
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Hello from Laura"))
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:58589", "/", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
@ -382,7 +382,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
Port = 51206,
}
},
UpstreamPathTemplate = "/",
@ -391,7 +391,7 @@ namespace Ocelot.AcceptanceTests
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products", 200, "Hello from Laura"))
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51206", "/api/products", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
@ -416,7 +416,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
Port = 51990,
}
},
UpstreamPathTemplate = "/",
@ -425,7 +425,7 @@ namespace Ocelot.AcceptanceTests
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products", 200, "Hello from Laura"))
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51990", "/api/products", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
@ -450,7 +450,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
Port = 58804,
}
},
UpstreamPathTemplate = "/products/",
@ -459,7 +459,7 @@ namespace Ocelot.AcceptanceTests
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/products", 200, "Hello from Laura"))
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:58804", "/products", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products"))
@ -484,7 +484,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
Port = 54015,
}
},
UpstreamPathTemplate = "/products",
@ -493,7 +493,7 @@ namespace Ocelot.AcceptanceTests
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/products", 200, "Hello from Laura"))
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:54015", "/products", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/"))
@ -517,7 +517,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
Port = 54072,
}
},
UpstreamPathTemplate = "/products/{productId}",
@ -526,7 +526,7 @@ namespace Ocelot.AcceptanceTests
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/products", 200, "Hello from Laura"))
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:54072", "/products", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/"))
@ -550,7 +550,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
Port = 55961,
}
},
UpstreamPathTemplate = "/products/{productId}",
@ -559,7 +559,7 @@ namespace Ocelot.AcceptanceTests
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product"))
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:55961", "/api/products/1", 200, "Some Product"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/1"))
@ -584,7 +584,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
Port = 51116,
}
},
UpstreamPathTemplate = "/{variantId}/products/{productId}",
@ -593,7 +593,7 @@ namespace Ocelot.AcceptanceTests
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/23/products/1", 200, "Some Product"))
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51116", "/api/23/products/1", 200, "Some Product"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("23/products/1"))
@ -618,7 +618,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
Port = 51809,
}
},
UpstreamPathTemplate = "/products/{productId}",
@ -627,7 +627,7 @@ namespace Ocelot.AcceptanceTests
}
};
this.Given(x => GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product"))
this.Given(x => GivenThereIsAServiceRunningOn("http://localhost:51809", "/api/products/1", 200, "Some Product"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/1"))
@ -650,7 +650,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
Port = 56615,
}
},
DownstreamScheme = "http",
@ -660,7 +660,7 @@ namespace Ocelot.AcceptanceTests
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 201, string.Empty))
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:56615", "/", 201, string.Empty))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenThePostHasContent("postContent"))
@ -686,7 +686,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
Port = 57771,
}
},
UpstreamHttpMethod = new List<string> { "Get" },
@ -694,7 +694,7 @@ namespace Ocelot.AcceptanceTests
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/newThing", 200, "Hello from Laura"))
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:57771", "/newThing", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/newThing?DeviceType=IphoneApp&Browser=moonpigIphone&BrowserString=-&CountryCode=123&DeviceName=iPhone 5 (GSM+CDMA)&OperatingSystem=iPhone OS 7.1.2&BrowserVersion=3708AdHoc&ipAddress=-"))
@ -719,7 +719,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
Port = 55609,
}
},
UpstreamPathTemplate = "/myApp1Name/api/{urlPath}",
@ -728,7 +728,7 @@ namespace Ocelot.AcceptanceTests
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product"))
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:55609", "/api/products/1", 200, "Some Product"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/myApp1Name/api/products/1"))
@ -752,7 +752,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
Port = 59911,
}
},
DownstreamScheme = "http",
@ -762,7 +762,7 @@ namespace Ocelot.AcceptanceTests
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "", 201, string.Empty))
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:59911", "", 201, string.Empty))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenThePostHasContent("postContent"))
@ -786,7 +786,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
Port = 59187,
}
},
DownstreamScheme = "http",
@ -796,7 +796,7 @@ namespace Ocelot.AcceptanceTests
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Hello from Laura"))
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:59187", "/", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
@ -821,7 +821,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
Port = 54079,
}
},
UpstreamPathTemplate = "/vacancy/",
@ -837,7 +837,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
Port = 54079,
}
},
UpstreamPathTemplate = "/vacancy/{vacancyId}",
@ -847,7 +847,7 @@ namespace Ocelot.AcceptanceTests
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/v1/vacancy/1", 200, "Hello from Laura"))
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:54079", "/api/v1/vacancy/1", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("api/vacancy/1"))

View File

@ -10,12 +10,14 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Moq;
using Newtonsoft.Json;
using Ocelot.Cache.CacheManager;
using Ocelot.Configuration.Creator;
using Ocelot.Configuration.File;
using Ocelot.DependencyInjection;
using Ocelot.Infrastructure;
using Ocelot.Logging;
using Ocelot.Middleware;
using Ocelot.Middleware.Multiplexer;
using Ocelot.Provider.Consul;
@ -1120,5 +1122,60 @@
_ocelotClient = _ocelotServer.CreateClient();
}
public void GivenOcelotIsRunningWithLogger()
{
_webHostBuilder = new WebHostBuilder();
_webHostBuilder
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json", false, false);
config.AddEnvironmentVariables();
})
.ConfigureServices(s =>
{
s.AddOcelot();
s.AddSingleton<IOcelotLoggerFactory, MockLoggerFactory>();
})
.Configure(app =>
{
app.UseOcelot().Wait();
});
_ocelotServer = new TestServer(_webHostBuilder);
_ocelotClient = _ocelotServer.CreateClient();
}
public void ThenWarningShouldBeLogged()
{
MockLoggerFactory loggerFactory = (MockLoggerFactory)_ocelotServer.Host.Services.GetService<IOcelotLoggerFactory>();
loggerFactory.Verify();
}
internal class MockLoggerFactory : IOcelotLoggerFactory
{
private Mock<IOcelotLogger> _logger;
public IOcelotLogger CreateLogger<T>()
{
if (_logger == null)
{
_logger = new Mock<IOcelotLogger>();
_logger.Setup(x => x.LogWarning(It.IsAny<string>())).Verifiable();
}
return _logger.Object;
}
public void Verify()
{
_logger.Verify(x => x.LogWarning(It.IsAny<string>()), Times.Once);
}
}
}
}

View File

@ -2,7 +2,7 @@
<PropertyGroup>
<VersionPrefix>0.0.0-dev</VersionPrefix>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AssemblyName>Ocelot.Benchmarks</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>Ocelot.Benchmarks</PackageId>
@ -25,6 +25,6 @@
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-19367-01" />
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
</ItemGroup>
</Project>

File diff suppressed because it is too large Load Diff

View File

@ -1,199 +1,199 @@
using Xunit;
[assembly: CollectionBehavior(DisableTestParallelization = true)]
namespace Ocelot.IntegrationTests
{
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Ocelot.Configuration.File;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
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;
public class HeaderTests : IDisposable
{
private readonly HttpClient _httpClient;
private IWebHost _builder;
private IWebHostBuilder _webHostBuilder;
private readonly string _ocelotBaseUrl;
private IWebHost _downstreamBuilder;
private HttpResponseMessage _response;
public HeaderTests()
{
_httpClient = new HttpClient();
_ocelotBaseUrl = "http://localhost:5005";
_httpClient.BaseAddress = new Uri(_ocelotBaseUrl);
}
[Fact]
public void should_pass_remote_ip_address_if_as_x_forwarded_for_header()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 6773,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
UpstreamHeaderTransform = new Dictionary<string,string>
{
{"X-Forwarded-For", "{RemoteIpAddress}"}
},
HttpHandlerOptions = new FileHttpHandlerOptions
{
AllowAutoRedirect = false
}
}
}
};
this.Given(x => GivenThereIsAServiceRunningOn("http://localhost:6773", 200, "X-Forwarded-For"))
.And(x => GivenThereIsAConfiguration(configuration))
.And(x => GivenOcelotIsRunning())
.When(x => WhenIGetUrlOnTheApiGateway("/"))
.Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => ThenXForwardedForIsSet())
.BDDfy();
}
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string headerKey)
{
_downstreamBuilder = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
{
app.Run(async context =>
{
if (context.Request.Headers.TryGetValue(headerKey, out var values))
{
var result = values.First();
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(result);
}
});
})
.Build();
_downstreamBuilder.Start();
}
private void GivenOcelotIsRunning()
{
_webHostBuilder = new WebHostBuilder()
.UseUrls(_ocelotBaseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json", false, false);
config.AddEnvironmentVariables();
})
.ConfigureServices(x =>
{
x.AddOcelot();
})
.Configure(app =>
{
app.UseOcelot().Wait();
});
_builder = _webHostBuilder.Build();
_builder.Start();
}
private void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
{
var configurationPath = $"{Directory.GetCurrentDirectory()}/ocelot.json";
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
if (File.Exists(configurationPath))
{
File.Delete(configurationPath);
}
File.WriteAllText(configurationPath, jsonConfiguration);
var text = File.ReadAllText(configurationPath);
configurationPath = $"{AppContext.BaseDirectory}/ocelot.json";
if (File.Exists(configurationPath))
{
File.Delete(configurationPath);
}
File.WriteAllText(configurationPath, jsonConfiguration);
text = File.ReadAllText(configurationPath);
}
public async Task WhenIGetUrlOnTheApiGateway(string url)
{
var request = new HttpRequestMessage(HttpMethod.Get, url);
_response = await _httpClient.SendAsync(request);
}
private void ThenTheStatusCodeShouldBe(HttpStatusCode code)
{
_response.StatusCode.ShouldBe(code);
}
private void ThenXForwardedForIsSet()
{
var windowsOrMac = "::1";
var linux = "127.0.0.1";
var header = _response.Content.ReadAsStringAsync().Result;
bool passed = false;
if (header == windowsOrMac || header == linux)
{
passed = true;
}
passed.ShouldBeTrue();
}
public void Dispose()
{
_builder?.Dispose();
_httpClient?.Dispose();
_downstreamBuilder?.Dispose();
}
}
}
using Xunit;
[assembly: CollectionBehavior(DisableTestParallelization = true)]
namespace Ocelot.IntegrationTests
{
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Ocelot.Configuration.File;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
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;
public class HeaderTests : IDisposable
{
private readonly HttpClient _httpClient;
private IWebHost _builder;
private IWebHostBuilder _webHostBuilder;
private readonly string _ocelotBaseUrl;
private IWebHost _downstreamBuilder;
private HttpResponseMessage _response;
public HeaderTests()
{
_httpClient = new HttpClient();
_ocelotBaseUrl = "http://localhost:5010";
_httpClient.BaseAddress = new Uri(_ocelotBaseUrl);
}
[Fact]
public void should_pass_remote_ip_address_if_as_x_forwarded_for_header()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 6773,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
UpstreamHeaderTransform = new Dictionary<string,string>
{
{"X-Forwarded-For", "{RemoteIpAddress}"}
},
HttpHandlerOptions = new FileHttpHandlerOptions
{
AllowAutoRedirect = false
}
}
}
};
this.Given(x => GivenThereIsAServiceRunningOn("http://localhost:6773", 200, "X-Forwarded-For"))
.And(x => GivenThereIsAConfiguration(configuration))
.And(x => GivenOcelotIsRunning())
.When(x => WhenIGetUrlOnTheApiGateway("/"))
.Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => ThenXForwardedForIsSet())
.BDDfy();
}
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string headerKey)
{
_downstreamBuilder = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
{
app.Run(async context =>
{
if (context.Request.Headers.TryGetValue(headerKey, out var values))
{
var result = values.First();
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(result);
}
});
})
.Build();
_downstreamBuilder.Start();
}
private void GivenOcelotIsRunning()
{
_webHostBuilder = new WebHostBuilder()
.UseUrls(_ocelotBaseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json", false, false);
config.AddEnvironmentVariables();
})
.ConfigureServices(x =>
{
x.AddOcelot();
})
.Configure(app =>
{
app.UseOcelot().Wait();
});
_builder = _webHostBuilder.Build();
_builder.Start();
}
private void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
{
var configurationPath = $"{Directory.GetCurrentDirectory()}/ocelot.json";
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
if (File.Exists(configurationPath))
{
File.Delete(configurationPath);
}
File.WriteAllText(configurationPath, jsonConfiguration);
var text = File.ReadAllText(configurationPath);
configurationPath = $"{AppContext.BaseDirectory}/ocelot.json";
if (File.Exists(configurationPath))
{
File.Delete(configurationPath);
}
File.WriteAllText(configurationPath, jsonConfiguration);
text = File.ReadAllText(configurationPath);
}
public async Task WhenIGetUrlOnTheApiGateway(string url)
{
var request = new HttpRequestMessage(HttpMethod.Get, url);
_response = await _httpClient.SendAsync(request);
}
private void ThenTheStatusCodeShouldBe(HttpStatusCode code)
{
_response.StatusCode.ShouldBe(code);
}
private void ThenXForwardedForIsSet()
{
var windowsOrMac = "::1";
var linux = "127.0.0.1";
var header = _response.Content.ReadAsStringAsync().Result;
bool passed = false;
if (header == windowsOrMac || header == linux)
{
passed = true;
}
passed.ShouldBeTrue();
}
public void Dispose()
{
_builder?.Dispose();
_httpClient?.Dispose();
_downstreamBuilder?.Dispose();
}
}
}

View File

@ -1,61 +1,61 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<VersionPrefix>0.0.0-dev</VersionPrefix>
<TargetFramework>netcoreapp3.0</TargetFramework>
<AssemblyName>Ocelot.IntegrationTests</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>Ocelot.IntegrationTests</PackageId>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<None Update="peers.json;appsettings.json;idsrv3test.pfx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Ocelot\Ocelot.csproj" />
<ProjectReference Include="..\..\src\Ocelot.Administration\Ocelot.Administration.csproj" />
<ProjectReference Include="..\..\src\Ocelot.Cache.CacheManager\Ocelot.Cache.CacheManager.csproj" />
<ProjectReference Include="..\..\src\Ocelot.Provider.Consul\Ocelot.Provider.Consul.csproj" />
<ProjectReference Include="..\..\src\Ocelot.Provider.Eureka\Ocelot.Provider.Eureka.csproj" />
<ProjectReference Include="..\..\src\Ocelot.Provider.Polly\Ocelot.Provider.Polly.csproj" />
<ProjectReference Include="..\..\src\Ocelot.Provider.Rafty\Ocelot.Provider.Rafty.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
<PackageReference Include="Microsoft.Data.SQLite" Version="3.0.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.66">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.0.0" />
<PackageReference Include="Microsoft.DotNet.InternalAbstractions" Version="1.0.500-preview2-1-003177" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="Shouldly" Version="4.0.0-beta0002" />
<PackageReference Include="TestStack.BDDfy" Version="4.3.2" />
<PackageReference Include="Microsoft.Data.SQLite" Version="3.0.0" />
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
<PackageReference Include="IdentityServer4" Version="3.0.1" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-19367-01" />
</ItemGroup>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<VersionPrefix>0.0.0-dev</VersionPrefix>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AssemblyName>Ocelot.IntegrationTests</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>Ocelot.IntegrationTests</PackageId>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<None Update="peers.json;appsettings.json;idsrv3test.pfx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Ocelot\Ocelot.csproj" />
<ProjectReference Include="..\..\src\Ocelot.Administration\Ocelot.Administration.csproj" />
<ProjectReference Include="..\..\src\Ocelot.Cache.CacheManager\Ocelot.Cache.CacheManager.csproj" />
<ProjectReference Include="..\..\src\Ocelot.Provider.Consul\Ocelot.Provider.Consul.csproj" />
<ProjectReference Include="..\..\src\Ocelot.Provider.Eureka\Ocelot.Provider.Eureka.csproj" />
<ProjectReference Include="..\..\src\Ocelot.Provider.Polly\Ocelot.Provider.Polly.csproj" />
<ProjectReference Include="..\..\src\Ocelot.Provider.Rafty\Ocelot.Provider.Rafty.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
<PackageReference Include="Microsoft.Data.SQLite" Version="3.0.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.66">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.0.0" />
<PackageReference Include="Microsoft.DotNet.InternalAbstractions" Version="1.0.500-preview2-1-003177" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="Shouldly" Version="4.0.0-beta0002" />
<PackageReference Include="TestStack.BDDfy" Version="4.3.2" />
<PackageReference Include="Microsoft.Data.SQLite" Version="3.0.0" />
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
<PackageReference Include="IdentityServer4" Version="3.0.1" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
</ItemGroup>
</Project>

View File

@ -1,204 +1,204 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Ocelot.Configuration.File;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using Shouldly;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.IntegrationTests
{
public class ThreadSafeHeadersTests : IDisposable
{
private readonly HttpClient _httpClient;
private IWebHost _builder;
private IWebHostBuilder _webHostBuilder;
private readonly string _ocelotBaseUrl;
private IWebHost _downstreamBuilder;
private readonly Random _random;
private readonly ConcurrentBag<ThreadSafeHeadersTestResult> _results;
public ThreadSafeHeadersTests()
{
_results = new ConcurrentBag<ThreadSafeHeadersTestResult>();
_random = new Random();
_httpClient = new HttpClient();
_ocelotBaseUrl = "http://localhost:5001";
_httpClient.BaseAddress = new Uri(_ocelotBaseUrl);
}
[Fact]
public void should_return_same_response_for_each_different_header_under_load_to_downsteam_service()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
},
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
},
},
};
this.Given(x => GivenThereIsAConfiguration(configuration))
.And(x => GivenThereIsAServiceRunningOn("http://localhost:51879"))
.And(x => GivenOcelotIsRunning())
.When(x => WhenIGetUrlOnTheApiGatewayMultipleTimesWithDifferentHeaderValues("/", 300))
.Then(x => ThenTheSameHeaderValuesAreReturnedByTheDownstreamService())
.BDDfy();
}
private void GivenThereIsAServiceRunningOn(string url)
{
_downstreamBuilder = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
{
app.Run(async context =>
{
var header = context.Request.Headers["ThreadSafeHeadersTest"];
context.Response.StatusCode = 200;
await context.Response.WriteAsync(header[0]);
});
})
.Build();
_downstreamBuilder.Start();
}
private void GivenOcelotIsRunning()
{
_webHostBuilder = new WebHostBuilder()
.UseUrls(_ocelotBaseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json", false, false);
config.AddEnvironmentVariables();
})
.ConfigureServices(x =>
{
x.AddOcelot();
})
.Configure(app =>
{
app.UseOcelot().Wait();
});
_builder = _webHostBuilder.Build();
_builder.Start();
}
private void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
{
var configurationPath = $"{Directory.GetCurrentDirectory()}/ocelot.json";
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
if (File.Exists(configurationPath))
{
File.Delete(configurationPath);
}
File.WriteAllText(configurationPath, jsonConfiguration);
var text = File.ReadAllText(configurationPath);
configurationPath = $"{AppContext.BaseDirectory}/ocelot.json";
if (File.Exists(configurationPath))
{
File.Delete(configurationPath);
}
File.WriteAllText(configurationPath, jsonConfiguration);
text = File.ReadAllText(configurationPath);
}
private void WhenIGetUrlOnTheApiGatewayMultipleTimesWithDifferentHeaderValues(string url, int times)
{
var tasks = new Task[times];
for (int i = 0; i < times; i++)
{
var urlCopy = url;
var random = _random.Next(0, 50);
tasks[i] = GetForThreadSafeHeadersTest(urlCopy, random);
}
Task.WaitAll(tasks);
}
private async Task GetForThreadSafeHeadersTest(string url, int random)
{
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Add("ThreadSafeHeadersTest", new List<string> { random.ToString() });
var response = await _httpClient.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
int result = int.Parse(content);
var tshtr = new ThreadSafeHeadersTestResult(result, random);
_results.Add(tshtr);
}
private void ThenTheSameHeaderValuesAreReturnedByTheDownstreamService()
{
foreach (var result in _results)
{
result.Result.ShouldBe(result.Random);
}
}
public void Dispose()
{
_builder?.Dispose();
_httpClient?.Dispose();
_downstreamBuilder?.Dispose();
}
private class ThreadSafeHeadersTestResult
{
public ThreadSafeHeadersTestResult(int result, int random)
{
Result = result;
Random = random;
}
public int Result { get; private set; }
public int Random { get; private set; }
}
}
}
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Ocelot.Configuration.File;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using Shouldly;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.IntegrationTests
{
public class ThreadSafeHeadersTests : IDisposable
{
private readonly HttpClient _httpClient;
private IWebHost _builder;
private IWebHostBuilder _webHostBuilder;
private readonly string _ocelotBaseUrl;
private IWebHost _downstreamBuilder;
private readonly Random _random;
private readonly ConcurrentBag<ThreadSafeHeadersTestResult> _results;
public ThreadSafeHeadersTests()
{
_results = new ConcurrentBag<ThreadSafeHeadersTestResult>();
_random = new Random();
_httpClient = new HttpClient();
_ocelotBaseUrl = "http://localhost:5001";
_httpClient.BaseAddress = new Uri(_ocelotBaseUrl);
}
[Fact]
public void should_return_same_response_for_each_different_header_under_load_to_downsteam_service()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51611,
},
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
},
},
};
this.Given(x => GivenThereIsAConfiguration(configuration))
.And(x => GivenThereIsAServiceRunningOn("http://localhost:51611"))
.And(x => GivenOcelotIsRunning())
.When(x => WhenIGetUrlOnTheApiGatewayMultipleTimesWithDifferentHeaderValues("/", 300))
.Then(x => ThenTheSameHeaderValuesAreReturnedByTheDownstreamService())
.BDDfy();
}
private void GivenThereIsAServiceRunningOn(string url)
{
_downstreamBuilder = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
{
app.Run(async context =>
{
var header = context.Request.Headers["ThreadSafeHeadersTest"];
context.Response.StatusCode = 200;
await context.Response.WriteAsync(header[0]);
});
})
.Build();
_downstreamBuilder.Start();
}
private void GivenOcelotIsRunning()
{
_webHostBuilder = new WebHostBuilder()
.UseUrls(_ocelotBaseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json", false, false);
config.AddEnvironmentVariables();
})
.ConfigureServices(x =>
{
x.AddOcelot();
})
.Configure(app =>
{
app.UseOcelot().Wait();
});
_builder = _webHostBuilder.Build();
_builder.Start();
}
private void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
{
var configurationPath = $"{Directory.GetCurrentDirectory()}/ocelot.json";
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
if (File.Exists(configurationPath))
{
File.Delete(configurationPath);
}
File.WriteAllText(configurationPath, jsonConfiguration);
var text = File.ReadAllText(configurationPath);
configurationPath = $"{AppContext.BaseDirectory}/ocelot.json";
if (File.Exists(configurationPath))
{
File.Delete(configurationPath);
}
File.WriteAllText(configurationPath, jsonConfiguration);
text = File.ReadAllText(configurationPath);
}
private void WhenIGetUrlOnTheApiGatewayMultipleTimesWithDifferentHeaderValues(string url, int times)
{
var tasks = new Task[times];
for (int i = 0; i < times; i++)
{
var urlCopy = url;
var random = _random.Next(0, 50);
tasks[i] = GetForThreadSafeHeadersTest(urlCopy, random);
}
Task.WaitAll(tasks);
}
private async Task GetForThreadSafeHeadersTest(string url, int random)
{
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Add("ThreadSafeHeadersTest", new List<string> { random.ToString() });
var response = await _httpClient.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
int result = int.Parse(content);
var tshtr = new ThreadSafeHeadersTestResult(result, random);
_results.Add(tshtr);
}
private void ThenTheSameHeaderValuesAreReturnedByTheDownstreamService()
{
foreach (var result in _results)
{
result.Result.ShouldBe(result.Random);
}
}
public void Dispose()
{
_builder?.Dispose();
_httpClient?.Dispose();
_downstreamBuilder?.Dispose();
}
private class ThreadSafeHeadersTestResult
{
public ThreadSafeHeadersTestResult(int result, int random)
{
Result = result;
Random = random;
}
public int Result { get; private set; }
public int Random { get; private set; }
}
}
}

View File

@ -1,45 +1,45 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<VersionPrefix>0.0.0-dev</VersionPrefix>
<TargetFramework>netcoreapp3.0</TargetFramework>
<PreserveCompilationContext>true</PreserveCompilationContext>
<AssemblyName>Ocelot.ManualTest</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>Ocelot.ManualTest</PackageId>
<RuntimeIdentifiers>osx.10.11-x64;osx.10.12-x64;win7-x64;win10-x64</RuntimeIdentifiers>
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<None Update="Views;Areas\**\Views">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
</ItemGroup>
<ItemGroup>
<None Update="ocelot.json">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
</ItemGroup>
<ItemGroup>
<None Update="ocelot.json;appsettings.json;idsrv3test.pfx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Ocelot\Ocelot.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.0.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.66">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-19367-01" />
</ItemGroup>
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<VersionPrefix>0.0.0-dev</VersionPrefix>
<TargetFramework>netcoreapp3.1</TargetFramework>
<PreserveCompilationContext>true</PreserveCompilationContext>
<AssemblyName>Ocelot.ManualTest</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>Ocelot.ManualTest</PackageId>
<RuntimeIdentifiers>osx.10.11-x64;osx.10.12-x64;win7-x64;win10-x64</RuntimeIdentifiers>
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<None Update="Views;Areas\**\Views">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
</ItemGroup>
<ItemGroup>
<None Update="ocelot.json">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
</ItemGroup>
<ItemGroup>
<None Update="ocelot.json;appsettings.json;idsrv3test.pfx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Ocelot\Ocelot.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.0.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.66">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
</ItemGroup>
</Project>

View File

@ -41,14 +41,14 @@ namespace Ocelot.UnitTests.Configuration
_internalConfigCreator = new Mock<IInternalConfigurationCreator>();
_internalConfigCreator.Setup(x => x.Create(It.IsAny<FileConfiguration>())).ReturnsAsync(new OkResponse<IInternalConfiguration>(_internalConfig));
_poller = new FileConfigurationPoller(_factory.Object, _repo.Object, _config.Object, _internalConfigRepo.Object, _internalConfigCreator.Object);
_poller.StartAsync(new CancellationToken());
}
[Fact]
public void should_start()
{
this.Given(x => ThenTheSetterIsCalled(_fileConfig, 1))
.BDDfy();
this.Given(x => GivenPollerHasStarted())
.Given(x => ThenTheSetterIsCalled(_fileConfig, 1))
.BDDfy();
}
[Fact]
@ -71,7 +71,8 @@ namespace Ocelot.UnitTests.Configuration
}
};
this.Given(x => WhenTheConfigIsChanged(newConfig, 0))
this.Given(x => GivenPollerHasStarted())
.Given(x => WhenTheConfigIsChanged(newConfig, 0))
.Then(x => ThenTheSetterIsCalledAtLeast(newConfig, 1))
.BDDfy();
}
@ -96,7 +97,8 @@ namespace Ocelot.UnitTests.Configuration
}
};
this.Given(x => WhenTheConfigIsChanged(newConfig, 10))
this.Given(x => GivenPollerHasStarted())
.Given(x => WhenTheConfigIsChanged(newConfig, 10))
.Then(x => ThenTheSetterIsCalled(newConfig, 1))
.BDDfy();
}
@ -121,11 +123,24 @@ namespace Ocelot.UnitTests.Configuration
}
};
this.Given(x => WhenProviderErrors())
this.Given(x => GivenPollerHasStarted())
.Given(x => WhenProviderErrors())
.Then(x => ThenTheSetterIsCalled(newConfig, 0))
.BDDfy();
}
[Fact]
public void should_dispose_cleanly_without_starting()
{
this.When(x => WhenPollerIsDisposed())
.BDDfy();
}
private void GivenPollerHasStarted()
{
_poller.StartAsync(CancellationToken.None);
}
private void WhenProviderErrors()
{
_repo
@ -141,6 +156,11 @@ namespace Ocelot.UnitTests.Configuration
.ReturnsAsync(new OkResponse<FileConfiguration>(newConfig));
}
private void WhenPollerIsDisposed()
{
_poller.Dispose();
}
private void ThenTheSetterIsCalled(FileConfiguration fileConfig, int times)
{
var result = WaitFor(4000).Until(() =>

View File

@ -41,7 +41,7 @@ namespace Ocelot.UnitTests.Configuration
}
};
var expectedOptions = new HttpHandlerOptions(false, false, false, true);
var expectedOptions = new HttpHandlerOptions(false, false, false, true, int.MaxValue);
this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions())
@ -60,7 +60,7 @@ namespace Ocelot.UnitTests.Configuration
}
};
var expectedOptions = new HttpHandlerOptions(false, false, true, true);
var expectedOptions = new HttpHandlerOptions(false, false, true, true, int.MaxValue);
this.Given(x => GivenTheFollowing(fileReRoute))
.And(x => GivenARealTracer())
@ -73,7 +73,7 @@ namespace Ocelot.UnitTests.Configuration
public void should_create_options_with_useCookie_false_and_allowAutoRedirect_true_as_default()
{
var fileReRoute = new FileReRoute();
var expectedOptions = new HttpHandlerOptions(false, false, false, true);
var expectedOptions = new HttpHandlerOptions(false, false, false, true, int.MaxValue);
this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions())
@ -94,7 +94,7 @@ namespace Ocelot.UnitTests.Configuration
}
};
var expectedOptions = new HttpHandlerOptions(false, false, false, true);
var expectedOptions = new HttpHandlerOptions(false, false, false, true, int.MaxValue);
this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions())
@ -110,7 +110,7 @@ namespace Ocelot.UnitTests.Configuration
HttpHandlerOptions = new FileHttpHandlerOptions()
};
var expectedOptions = new HttpHandlerOptions(false, false, false, true);
var expectedOptions = new HttpHandlerOptions(false, false, false, true, int.MaxValue);
this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions())
@ -129,7 +129,64 @@ namespace Ocelot.UnitTests.Configuration
}
};
var expectedOptions = new HttpHandlerOptions(false, false, false, false);
var expectedOptions = new HttpHandlerOptions(false, false, false, false, int.MaxValue);
this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions())
.Then(x => ThenTheFollowingOptionsReturned(expectedOptions))
.BDDfy();
}
[Fact]
public void should_create_options_with_specified_MaxConnectionsPerServer()
{
var fileReRoute = new FileReRoute
{
HttpHandlerOptions = new FileHttpHandlerOptions
{
MaxConnectionsPerServer = 10
}
};
var expectedOptions = new HttpHandlerOptions(false, false, false, true, 10);
this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions())
.Then(x => ThenTheFollowingOptionsReturned(expectedOptions))
.BDDfy();
}
[Fact]
public void should_create_options_fixing_specified_MaxConnectionsPerServer_range()
{
var fileReRoute = new FileReRoute
{
HttpHandlerOptions = new FileHttpHandlerOptions
{
MaxConnectionsPerServer = -1
}
};
var expectedOptions = new HttpHandlerOptions(false, false, false, true, int.MaxValue);
this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions())
.Then(x => ThenTheFollowingOptionsReturned(expectedOptions))
.BDDfy();
}
[Fact]
public void should_create_options_fixing_specified_MaxConnectionsPerServer_range_when_zero()
{
var fileReRoute = new FileReRoute
{
HttpHandlerOptions = new FileHttpHandlerOptions
{
MaxConnectionsPerServer = 0
}
};
var expectedOptions = new HttpHandlerOptions(false, false, false, true, int.MaxValue);
this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions())
@ -154,6 +211,7 @@ namespace Ocelot.UnitTests.Configuration
_httpHandlerOptions.UseCookieContainer.ShouldBe(expected.UseCookieContainer);
_httpHandlerOptions.UseTracing.ShouldBe(expected.UseTracing);
_httpHandlerOptions.UseProxy.ShouldBe(expected.UseProxy);
_httpHandlerOptions.MaxConnectionsPerServer.ShouldBe(expected.MaxConnectionsPerServer);
}
private void GivenARealTracer()

View File

@ -50,7 +50,7 @@ namespace Ocelot.UnitTests.Configuration
};
var expected = new RateLimitOptionsBuilder()
.WithClientIdHeader("ClientIdHeader")
.WithClientWhiteList(fileReRoute.RateLimitOptions.ClientWhitelist)
.WithClientWhiteList(() => fileReRoute.RateLimitOptions.ClientWhitelist)
.WithDisableRateLimitHeaders(true)
.WithEnableRateLimiting(true)
.WithHttpStatusCode(200)

View File

@ -1341,6 +1341,32 @@
.BDDfy();
}
[Fact]
public void configuration_is_invalid_when_placeholder_is_used_twice_in_upstream_path_template()
{
this.Given(x => x.GivenAConfiguration(new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/bar/{everything}",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort() { Host = "a.b.cd" },
},
UpstreamPathTemplate = "/foo/bar/{everything}/{everything}",
UpstreamHttpMethod = new List<string> { "Get" },
},
},
}))
.When(x => x.WhenIValidateTheConfiguration())
.Then(x => x.ThenTheResultIsNotValid())
.And(x => x.ThenTheErrorMessageAtPositionIs(0, "reRoute /foo/bar/{everything}/{everything} has duplicated placeholder"))
.BDDfy();
}
private void GivenAConfiguration(FileConfiguration fileConfiguration)
{
_fileConfiguration = fileConfiguration;

View File

@ -350,6 +350,36 @@
.BDDfy();
}
[Fact]
public void should_not_replace_by_empty_scheme()
{
var downstreamReRoute = new DownstreamReRouteBuilder()
.WithDownstreamScheme("")
.WithServiceName("Ocelot/OcelotApp")
.WithUseServiceDiscovery(true)
.Build();
var downstreamRoute = new DownstreamRoute(
new List<PlaceholderNameAndValue>(),
new ReRouteBuilder()
.WithDownstreamReRoute(downstreamReRoute)
.Build());
var config = new ServiceProviderConfigurationBuilder()
.WithType("ServiceFabric")
.WithHost("localhost")
.WithPort(19081)
.Build();
this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
.And(x => GivenTheServiceProviderConfigIs(config))
.And(x => x.GivenTheDownstreamRequestUriIs("https://localhost:19081?PartitionKind=test&PartitionKey=1"))
.And(x => x.GivenTheUrlReplacerWillReturnSequence("/api/products/1", "Ocelot/OcelotApp"))
.When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheDownstreamRequestUriIs("https://localhost:19081/Ocelot/OcelotApp/api/products/1?PartitionKind=test&PartitionKey=1"))
.BDDfy();
}
private void GivenTheServiceProviderConfigIs(ServiceProviderConfiguration config)
{
var configuration = new InternalConfiguration(null, null, config, null, null, null, null, null);

View File

@ -1,3 +1,5 @@
using System;
using System.Linq.Expressions;
using Ocelot.Middleware;
namespace Ocelot.UnitTests.LoadBalancer
@ -108,6 +110,26 @@ namespace Ocelot.UnitTests.LoadBalancer
.BDDfy();
}
[Fact]
public void should_set_scheme()
{
var downstreamRoute = new DownstreamReRouteBuilder()
.WithUpstreamHttpMethod(new List<string> { "Get" })
.Build();
var serviceProviderConfig = new ServiceProviderConfigurationBuilder()
.Build();
this.Given(x => x.GivenTheDownStreamUrlIs("http://my.url/abc?q=123"))
.And(x => GivenTheConfigurationIs(serviceProviderConfig))
.And(x => x.GivenTheDownStreamRouteIs(downstreamRoute, new List<Ocelot.DownstreamRouteFinder.UrlMatcher.PlaceholderNameAndValue>()))
.And(x => x.GivenTheLoadBalancerHouseReturns())
.And(x => x.GivenTheLoadBalancerReturnsOk())
.When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenAnHostAndPortIsSetOnPipeline())
.BDDfy();
}
private void WhenICallTheMiddleware()
{
_middleware = new LoadBalancingMiddleware(_next, _loggerFactory.Object, _loadBalancerHouse.Object);
@ -135,6 +157,13 @@ namespace Ocelot.UnitTests.LoadBalancer
.ReturnsAsync(_getHostAndPortError);
}
private void GivenTheLoadBalancerReturnsOk()
{
_loadBalancer
.Setup(x => x.Lease(It.IsAny<DownstreamContext>()))
.ReturnsAsync(new OkResponse<ServiceHostAndPort>(new ServiceHostAndPort("abc", 123, "https")));
}
private void GivenTheLoadBalancerReturns()
{
_hostAndPort = new ServiceHostAndPort("127.0.0.1", 80);
@ -186,6 +215,13 @@ namespace Ocelot.UnitTests.LoadBalancer
_downstreamContext.Errors.ShouldBe(_getHostAndPortError.Errors);
}
private void ThenAnHostAndPortIsSetOnPipeline()
{
_downstreamContext.DownstreamRequest.Host.ShouldBeEquivalentTo("abc");
_downstreamContext.DownstreamRequest.Port.ShouldBeEquivalentTo(123);
_downstreamContext.DownstreamRequest.Scheme.ShouldBeEquivalentTo("https");
}
private void ThenTheDownstreamUrlIsReplacedWith(string expectedUri)
{
_downstreamContext.DownstreamRequest.ToHttpRequestMessage().RequestUri.OriginalString.ShouldBe(expectedUri);

Some files were not shown because too many files have changed in this diff Show More