Merge remote-tracking branch 'origin/develop' into feature/603-bad-gateway

This commit is contained in:
jlukawska 2020-01-22 14:58:04 +01:00
commit 8985495f38
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 # Contributor Covenant Code of Conduct
## Our Pledge ## 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. 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 ## Our Standards
Examples of behavior that contributes to creating a positive environment include: Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language * Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences * Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism * Gracefully accepting constructive criticism
* Focusing on what is best for the community * Focusing on what is best for the community
* Showing empathy towards other community members * Showing empathy towards other community members
Examples of unacceptable behavior by participants include: Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances * The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks * Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment * Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission * 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 * Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities ## 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 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. 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 ## 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. 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 ## 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. 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. 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 ## 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] 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 [homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/ [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 :) We love to receive contributions from the community so please keep them coming :)
Pull requests, issues and commentary welcome! 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 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 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 :) 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 :) 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 ## Expected Behavior / New Feature
## Actual Behavior / Motivation for New Feature ## Actual Behavior / Motivation for New Feature
## Steps to Reproduce the Problem ## Steps to Reproduce the Problem
1. 1.
1. 1.
1. 1.
## Specifications ## Specifications
- Version: - Version:
- Platform: - Platform:
- Subsystem: - Subsystem:

View File

@ -1,7 +1,7 @@
Fixes / New Feature # Fixes / New Feature #
## Proposed Changes ## 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> <Project>
<PropertyGroup> <PropertyGroup>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<RepositoryType>git</RepositoryType>
<RepositoryType>git</RepositoryType> <RepositoryUrl>https://github.com/ThreeMammals/Ocelot</RepositoryUrl>
<RepositoryUrl>https://github.com/ThreeMammals/Ocelot</RepositoryUrl> <!-- Optional: Publish the repository URL in the built .nupkg (in the NuSpec <Repository> element) -->
<!-- Optional: Publish the repository URL in the built .nupkg (in the NuSpec <Repository> element) --> <PublishRepositoryUrl>true</PublishRepositoryUrl>
<PublishRepositoryUrl>true</PublishRepositoryUrl> <!-- Optional: Build symbol package (.snupkg) to distribute the PDB containing Source Link -->
<!-- Optional: Build symbol package (.snupkg) to distribute the PDB containing Source Link --> <IncludeSymbols>true</IncludeSymbols>
<IncludeSymbols>true</IncludeSymbols> <SymbolPackageFormat>snupkg</SymbolPackageFormat>
<SymbolPackageFormat>snupkg</SymbolPackageFormat> </PropertyGroup>
</PropertyGroup> <ItemGroup>
<ItemGroup> <!-- <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/> -->
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-18618-05" PrivateAssets="All"/> </ItemGroup>
</ItemGroup> </Project>
</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 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15 # Visual Studio Version 16
VisualStudioVersion = 15.0.27130.2036 VisualStudioVersion = 16.0.29613.14
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5CFB79B7-C9DC-45A4-9A75-625D92471702}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5CFB79B7-C9DC-45A4-9A75-625D92471702}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3FA7C349-DBE8-4904-A2CE-015B8869CE6C}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3FA7C349-DBE8-4904-A2CE-015B8869CE6C}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
.dockerignore = .dockerignore .dockerignore = .dockerignore
.gitignore = .gitignore .gitignore = .gitignore
build-and-release-unstable.ps1 = build-and-release-unstable.ps1 build.cake = build.cake
build-and-run-tests.ps1 = build-and-run-tests.ps1 build.ps1 = build.ps1
build.cake = build.cake codeanalysis.ruleset = codeanalysis.ruleset
build.ps1 = build.ps1 GitVersion.yml = GitVersion.yml
codeanalysis.ruleset = codeanalysis.ruleset LICENSE.md = LICENSE.md
docker-compose.yaml = docker-compose.yaml README.md = README.md
Dockerfile = Dockerfile ReleaseNotes.md = ReleaseNotes.md
GitVersion.yml = GitVersion.yml EndProjectSection
global.json = global.json EndProject
LICENSE.md = LICENSE.md Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{5B401523-36DA-4491-B73A-7590A26E420B}"
README.md = README.md EndProject
release.ps1 = release.ps1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot", "src\Ocelot\Ocelot.csproj", "{D6DF4206-0DBA-41D8-884D-C3E08290FDBB}"
ReleaseNotes.md = ReleaseNotes.md EndProject
run-acceptance-tests.ps1 = run-acceptance-tests.ps1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.UnitTests", "test\Ocelot.UnitTests\Ocelot.UnitTests.csproj", "{54E84F1A-E525-4443-96EC-039CBD50C263}"
run-benchmarks.ps1 = run-benchmarks.ps1 EndProject
run-unit-tests.ps1 = run-unit-tests.ps1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.AcceptanceTests", "test\Ocelot.AcceptanceTests\Ocelot.AcceptanceTests.csproj", "{F8C224FE-36BE-45F5-9B0E-666D8F4A9B52}"
version.ps1 = version.ps1 EndProject
EndProjectSection Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.ManualTest", "test\Ocelot.ManualTest\Ocelot.ManualTest.csproj", "{02BBF4C5-517E-4157-8D21-4B8B9E118B7A}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{5B401523-36DA-4491-B73A-7590A26E420B}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Benchmarks", "test\Ocelot.Benchmarks\Ocelot.Benchmarks.csproj", "{106B49E6-95F6-4A7B-B81C-96BFA74AF035}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot", "src\Ocelot\Ocelot.csproj", "{D6DF4206-0DBA-41D8-884D-C3E08290FDBB}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.IntegrationTests", "test\Ocelot.IntegrationTests\Ocelot.IntegrationTests.csproj", "{D4575572-99CA-4530-8737-C296EDA326F8}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.UnitTests", "test\Ocelot.UnitTests\Ocelot.UnitTests.csproj", "{54E84F1A-E525-4443-96EC-039CBD50C263}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Administration", "src\Ocelot.Administration\Ocelot.Administration.csproj", "{F69CEF43-27D2-4940-A47A-FCA879E371BC}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.AcceptanceTests", "test\Ocelot.AcceptanceTests\Ocelot.AcceptanceTests.csproj", "{F8C224FE-36BE-45F5-9B0E-666D8F4A9B52}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Cache.CacheManager", "src\Ocelot.Cache.CacheManager\Ocelot.Cache.CacheManager.csproj", "{EB9F438F-062E-499F-B6EA-4412BEF6D74C}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.ManualTest", "test\Ocelot.ManualTest\Ocelot.ManualTest.csproj", "{02BBF4C5-517E-4157-8D21-4B8B9E118B7A}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Consul", "src\Ocelot.Provider.Consul\Ocelot.Provider.Consul.csproj", "{02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Benchmarks", "test\Ocelot.Benchmarks\Ocelot.Benchmarks.csproj", "{106B49E6-95F6-4A7B-B81C-96BFA74AF035}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Eureka", "src\Ocelot.Provider.Eureka\Ocelot.Provider.Eureka.csproj", "{9BBD3586-145C-4FA0-91C5-9ED58287D753}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.IntegrationTests", "test\Ocelot.IntegrationTests\Ocelot.IntegrationTests.csproj", "{D4575572-99CA-4530-8737-C296EDA326F8}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Polly", "src\Ocelot.Provider.Polly\Ocelot.Provider.Polly.csproj", "{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Administration", "src\Ocelot.Administration\Ocelot.Administration.csproj", "{F69CEF43-27D2-4940-A47A-FCA879E371BC}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Rafty", "src\Ocelot.Provider.Rafty\Ocelot.Provider.Rafty.csproj", "{AC153C67-EF18-47E6-A230-F0D3CF5F0A98}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Cache.CacheManager", "src\Ocelot.Cache.CacheManager\Ocelot.Cache.CacheManager.csproj", "{EB9F438F-062E-499F-B6EA-4412BEF6D74C}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Tracing.Butterfly", "src\Ocelot.Tracing.Butterfly\Ocelot.Tracing.Butterfly.csproj", "{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Consul", "src\Ocelot.Provider.Consul\Ocelot.Provider.Consul.csproj", "{02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Kubernetes", "src\Ocelot.Provider.Kubernetes\Ocelot.Provider.Kubernetes.csproj", "{72C8E528-B4F5-45CE-8A06-CD3787364856}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Eureka", "src\Ocelot.Provider.Eureka\Ocelot.Provider.Eureka.csproj", "{9BBD3586-145C-4FA0-91C5-9ED58287D753}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{8FA0CBA0-0338-48EB-B37F-83CA5022237C}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Polly", "src\Ocelot.Provider.Polly\Ocelot.Provider.Polly.csproj", "{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OcelotBasic", "samples\OcelotBasic\OcelotBasic.csproj", "{ED0B3A09-112B-4BA4-82D6-11569BC7A99B}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Rafty", "src\Ocelot.Provider.Rafty\Ocelot.Provider.Rafty.csproj", "{AC153C67-EF18-47E6-A230-F0D3CF5F0A98}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AdministrationApi", "samples\AdministrationApi\AdministrationApi.csproj", "{B180F8AE-2F8F-44F9-9E5D-FE65B84B742E}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Tracing.Butterfly", "src\Ocelot.Tracing.Butterfly\Ocelot.Tracing.Butterfly.csproj", "{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OcelotGraphQL", "samples\OcelotGraphQL\OcelotGraphQL.csproj", "{F43429C3-EC49-464F-9423-9118A36E8FE3}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ocelot.Provider.Kubernetes", "src\Ocelot.Provider.Kubernetes\Ocelot.Provider.Kubernetes.csproj", "{72C8E528-B4F5-45CE-8A06-CD3787364856}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "eureka", "eureka", "{F1CF6F06-5A34-4A6A-8C19-003A78AB0DCF}"
EndProject EndProject
Global Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApiGateway", "samples\OcelotEureka\ApiGateway\ApiGateway.csproj", "{48B3DD3C-7F4D-40C1-A104-3BF9EF4ACE29}"
GlobalSection(SolutionConfigurationPlatforms) = preSolution EndProject
Debug|Any CPU = Debug|Any CPU Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DownstreamService", "samples\OcelotEureka\DownstreamService\DownstreamService.csproj", "{32ADF9B3-CBFA-4607-8A8E-1532D90A7197}"
Release|Any CPU = Release|Any CPU EndProject
EndGlobalSection Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "k8s", "k8s", "{4B706988-4817-43A8-ABE1-32A67998C2C8}"
GlobalSection(ProjectConfigurationPlatforms) = postSolution EndProject
{D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApiGateway", "samples\OcelotKube\ApiGateway\ApiGateway.csproj", "{8500055B-2C51-4CF1-A6EE-F05BB3E9BF16}"
{D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Debug|Any CPU.Build.0 = Debug|Any CPU EndProject
{D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Release|Any CPU.ActiveCfg = Release|Any CPU Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DownstreamService", "samples\OcelotKube\DownstreamService\DownstreamService.csproj", "{7B319B8C-8155-4779-BD93-5ABD05CA2AB6}"
{D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Release|Any CPU.Build.0 = Release|Any CPU EndProject
{54E84F1A-E525-4443-96EC-039CBD50C263}.Debug|Any CPU.ActiveCfg = Debug|Any CPU Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "service-fabric", "service-fabric", "{B412628F-C325-47E1-A8D9-873DE04C8AF5}"
{54E84F1A-E525-4443-96EC-039CBD50C263}.Debug|Any CPU.Build.0 = Debug|Any CPU EndProject
{54E84F1A-E525-4443-96EC-039CBD50C263}.Release|Any CPU.ActiveCfg = Release|Any CPU Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OcelotApplicationApiGateway", "samples\OcelotServiceFabric\src\OcelotApplicationApiGateway\OcelotApplicationApiGateway.csproj", "{8E6DAE6E-E9B1-433A-80C3-1E2640FBA590}"
{54E84F1A-E525-4443-96EC-039CBD50C263}.Release|Any CPU.Build.0 = Release|Any CPU EndProject
{F8C224FE-36BE-45F5-9B0E-666D8F4A9B52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OcelotApplicationService", "samples\OcelotServiceFabric\src\OcelotApplicationService\OcelotApplicationService.csproj", "{33BE6D88-F188-4E60-83AC-3C4B94D24675}"
{F8C224FE-36BE-45F5-9B0E-666D8F4A9B52}.Debug|Any CPU.Build.0 = Debug|Any CPU EndProject
{F8C224FE-36BE-45F5-9B0E-666D8F4A9B52}.Release|Any CPU.ActiveCfg = Release|Any CPU Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "administration", "administration", "{1F1F324D-6EA4-4E63-A6A7-C6053F412F1A}"
{F8C224FE-36BE-45F5-9B0E-666D8F4A9B52}.Release|Any CPU.Build.0 = Release|Any CPU EndProject
{02BBF4C5-517E-4157-8D21-4B8B9E118B7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "basic", "basic", "{ED066001-BAF7-4117-9884-DF591A56347D}"
{02BBF4C5-517E-4157-8D21-4B8B9E118B7A}.Debug|Any CPU.Build.0 = Debug|Any CPU EndProject
{02BBF4C5-517E-4157-8D21-4B8B9E118B7A}.Release|Any CPU.ActiveCfg = Release|Any CPU Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "graphql", "graphql", "{C15CD120-5F8D-41DE-9B21-00E3EA77D6C1}"
{02BBF4C5-517E-4157-8D21-4B8B9E118B7A}.Release|Any CPU.Build.0 = Release|Any CPU EndProject
{106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Debug|Any CPU.ActiveCfg = Debug|Any CPU Global
{106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Debug|Any CPU.Build.0 = Debug|Any CPU GlobalSection(SolutionConfigurationPlatforms) = preSolution
{106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Release|Any CPU.ActiveCfg = Release|Any CPU Debug|Any CPU = Debug|Any CPU
{106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Release|Any CPU.Build.0 = Release|Any CPU Release|Any CPU = Release|Any CPU
{D4575572-99CA-4530-8737-C296EDA326F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU EndGlobalSection
{D4575572-99CA-4530-8737-C296EDA326F8}.Debug|Any CPU.Build.0 = Debug|Any CPU GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D4575572-99CA-4530-8737-C296EDA326F8}.Release|Any CPU.ActiveCfg = Release|Any CPU {D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D4575572-99CA-4530-8737-C296EDA326F8}.Release|Any CPU.Build.0 = Release|Any CPU {D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F69CEF43-27D2-4940-A47A-FCA879E371BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F69CEF43-27D2-4940-A47A-FCA879E371BC}.Debug|Any CPU.Build.0 = Debug|Any CPU {D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Release|Any CPU.Build.0 = Release|Any CPU
{F69CEF43-27D2-4940-A47A-FCA879E371BC}.Release|Any CPU.ActiveCfg = Release|Any CPU {54E84F1A-E525-4443-96EC-039CBD50C263}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F69CEF43-27D2-4940-A47A-FCA879E371BC}.Release|Any CPU.Build.0 = Release|Any CPU {54E84F1A-E525-4443-96EC-039CBD50C263}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EB9F438F-062E-499F-B6EA-4412BEF6D74C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {54E84F1A-E525-4443-96EC-039CBD50C263}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EB9F438F-062E-499F-B6EA-4412BEF6D74C}.Debug|Any CPU.Build.0 = Debug|Any CPU {54E84F1A-E525-4443-96EC-039CBD50C263}.Release|Any CPU.Build.0 = Release|Any CPU
{EB9F438F-062E-499F-B6EA-4412BEF6D74C}.Release|Any CPU.ActiveCfg = Release|Any CPU {F8C224FE-36BE-45F5-9B0E-666D8F4A9B52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EB9F438F-062E-499F-B6EA-4412BEF6D74C}.Release|Any CPU.Build.0 = Release|Any CPU {F8C224FE-36BE-45F5-9B0E-666D8F4A9B52}.Debug|Any CPU.Build.0 = Debug|Any CPU
{02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F8C224FE-36BE-45F5-9B0E-666D8F4A9B52}.Release|Any CPU.ActiveCfg = Release|Any CPU
{02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0}.Debug|Any CPU.Build.0 = Debug|Any CPU {F8C224FE-36BE-45F5-9B0E-666D8F4A9B52}.Release|Any CPU.Build.0 = Release|Any CPU
{02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0}.Release|Any CPU.ActiveCfg = Release|Any CPU {02BBF4C5-517E-4157-8D21-4B8B9E118B7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0}.Release|Any CPU.Build.0 = Release|Any CPU {02BBF4C5-517E-4157-8D21-4B8B9E118B7A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9BBD3586-145C-4FA0-91C5-9ED58287D753}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {02BBF4C5-517E-4157-8D21-4B8B9E118B7A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9BBD3586-145C-4FA0-91C5-9ED58287D753}.Debug|Any CPU.Build.0 = Debug|Any CPU {02BBF4C5-517E-4157-8D21-4B8B9E118B7A}.Release|Any CPU.Build.0 = Release|Any CPU
{9BBD3586-145C-4FA0-91C5-9ED58287D753}.Release|Any CPU.ActiveCfg = Release|Any CPU {106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9BBD3586-145C-4FA0-91C5-9ED58287D753}.Release|Any CPU.Build.0 = Release|Any CPU {106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Debug|Any CPU.Build.0 = Debug|Any CPU {106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Release|Any CPU.Build.0 = Release|Any CPU
{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Release|Any CPU.ActiveCfg = Release|Any CPU {D4575572-99CA-4530-8737-C296EDA326F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Release|Any CPU.Build.0 = Release|Any CPU {D4575572-99CA-4530-8737-C296EDA326F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AC153C67-EF18-47E6-A230-F0D3CF5F0A98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D4575572-99CA-4530-8737-C296EDA326F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AC153C67-EF18-47E6-A230-F0D3CF5F0A98}.Debug|Any CPU.Build.0 = Debug|Any CPU {D4575572-99CA-4530-8737-C296EDA326F8}.Release|Any CPU.Build.0 = Release|Any CPU
{AC153C67-EF18-47E6-A230-F0D3CF5F0A98}.Release|Any CPU.ActiveCfg = Release|Any CPU {F69CEF43-27D2-4940-A47A-FCA879E371BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AC153C67-EF18-47E6-A230-F0D3CF5F0A98}.Release|Any CPU.Build.0 = Release|Any CPU {F69CEF43-27D2-4940-A47A-FCA879E371BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F69CEF43-27D2-4940-A47A-FCA879E371BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Debug|Any CPU.Build.0 = Debug|Any CPU {F69CEF43-27D2-4940-A47A-FCA879E371BC}.Release|Any CPU.Build.0 = Release|Any CPU
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Release|Any CPU.ActiveCfg = Release|Any CPU {EB9F438F-062E-499F-B6EA-4412BEF6D74C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Release|Any CPU.Build.0 = Release|Any CPU {EB9F438F-062E-499F-B6EA-4412BEF6D74C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{72C8E528-B4F5-45CE-8A06-CD3787364856}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EB9F438F-062E-499F-B6EA-4412BEF6D74C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{72C8E528-B4F5-45CE-8A06-CD3787364856}.Debug|Any CPU.Build.0 = Debug|Any CPU {EB9F438F-062E-499F-B6EA-4412BEF6D74C}.Release|Any CPU.Build.0 = Release|Any CPU
{72C8E528-B4F5-45CE-8A06-CD3787364856}.Release|Any CPU.ActiveCfg = Release|Any CPU {02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{72C8E528-B4F5-45CE-8A06-CD3787364856}.Release|Any CPU.Build.0 = Release|Any CPU {02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0}.Debug|Any CPU.Build.0 = Debug|Any CPU
EndGlobalSection {02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0}.Release|Any CPU.ActiveCfg = Release|Any CPU
GlobalSection(SolutionProperties) = preSolution {02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0}.Release|Any CPU.Build.0 = Release|Any CPU
HideSolutionNode = FALSE {9BBD3586-145C-4FA0-91C5-9ED58287D753}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
EndGlobalSection {9BBD3586-145C-4FA0-91C5-9ED58287D753}.Debug|Any CPU.Build.0 = Debug|Any CPU
GlobalSection(NestedProjects) = preSolution {9BBD3586-145C-4FA0-91C5-9ED58287D753}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D6DF4206-0DBA-41D8-884D-C3E08290FDBB} = {5CFB79B7-C9DC-45A4-9A75-625D92471702} {9BBD3586-145C-4FA0-91C5-9ED58287D753}.Release|Any CPU.Build.0 = Release|Any CPU
{54E84F1A-E525-4443-96EC-039CBD50C263} = {5B401523-36DA-4491-B73A-7590A26E420B} {1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F8C224FE-36BE-45F5-9B0E-666D8F4A9B52} = {5B401523-36DA-4491-B73A-7590A26E420B} {1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{02BBF4C5-517E-4157-8D21-4B8B9E118B7A} = {5B401523-36DA-4491-B73A-7590A26E420B} {1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{106B49E6-95F6-4A7B-B81C-96BFA74AF035} = {5B401523-36DA-4491-B73A-7590A26E420B} {1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Release|Any CPU.Build.0 = Release|Any CPU
{D4575572-99CA-4530-8737-C296EDA326F8} = {5B401523-36DA-4491-B73A-7590A26E420B} {AC153C67-EF18-47E6-A230-F0D3CF5F0A98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F69CEF43-27D2-4940-A47A-FCA879E371BC} = {5CFB79B7-C9DC-45A4-9A75-625D92471702} {AC153C67-EF18-47E6-A230-F0D3CF5F0A98}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EB9F438F-062E-499F-B6EA-4412BEF6D74C} = {5CFB79B7-C9DC-45A4-9A75-625D92471702} {AC153C67-EF18-47E6-A230-F0D3CF5F0A98}.Release|Any CPU.ActiveCfg = Release|Any CPU
{02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0} = {5CFB79B7-C9DC-45A4-9A75-625D92471702} {AC153C67-EF18-47E6-A230-F0D3CF5F0A98}.Release|Any CPU.Build.0 = Release|Any CPU
{9BBD3586-145C-4FA0-91C5-9ED58287D753} = {5CFB79B7-C9DC-45A4-9A75-625D92471702} {6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E} = {5CFB79B7-C9DC-45A4-9A75-625D92471702} {6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AC153C67-EF18-47E6-A230-F0D3CF5F0A98} = {5CFB79B7-C9DC-45A4-9A75-625D92471702} {6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F} = {5CFB79B7-C9DC-45A4-9A75-625D92471702} {6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Release|Any CPU.Build.0 = Release|Any CPU
{72C8E528-B4F5-45CE-8A06-CD3787364856} = {5CFB79B7-C9DC-45A4-9A75-625D92471702} {72C8E528-B4F5-45CE-8A06-CD3787364856}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
EndGlobalSection {72C8E528-B4F5-45CE-8A06-CD3787364856}.Debug|Any CPU.Build.0 = Debug|Any CPU
GlobalSection(ExtensibilityGlobals) = postSolution {72C8E528-B4F5-45CE-8A06-CD3787364856}.Release|Any CPU.ActiveCfg = Release|Any CPU
SolutionGuid = {21476EFF-778A-4F97-8A56-D1AF1CEC0C48} {72C8E528-B4F5-45CE-8A06-CD3787364856}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection {ED0B3A09-112B-4BA4-82D6-11569BC7A99B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
EndGlobal {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) [<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) [![CircleCI](https://circleci.com/gh/ThreeMammals/Ocelot/tree/master.svg?style=svg)](https://circleci.com/gh/ThreeMammals/Ocelot/tree/master)
[![Build Status](https://travis-ci.org/ThreeMammals/Ocelot.svg?branch=develop)](https://travis-ci.org/ThreeMammals/Ocelot) Linux & OSX (Travis) [![Coverage Status](https://coveralls.io/repos/github/ThreeMammals/Ocelot/badge.svg?branch=master)](https://coveralls.io/github/ThreeMammals/Ocelot?branch=master)
[![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) [Slack](threemammals.slack.com)
[![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
# Ocelot 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.
Ocelot is a .NET API Gateway. This project is aimed at people using .NET running In particular I want easy integration with
a micro services / service oriented architecture IdentityServer reference and bearer tokens.
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.
We have been unable to find this in my current workplace
In particular I want easy integration with without having to write our own Javascript middlewares
IdentityServer reference and bearer tokens. to handle the IdentityServer reference tokens. We would
rather use the IdentityServer code that already exists
We have been unable to find this in my current workplace to do this.
without having to write our own Javascript middlewares
to handle the IdentityServer reference tokens. We would Ocelot is a bunch of middlewares in a specific order.
rather use the IdentityServer code that already exists
to do this. 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
Ocelot is a bunch of middlewares in a specific order. 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.
Ocelot manipulates the HttpRequest object into a state specified by its configuration until The response from the downstream service is retrieved as the requests goes back up the Ocelot pipeline.
it reaches a request builder middleware where it creates a HttpRequestMessage object which is There is a piece of middleware that maps the HttpResponseMessage onto the HttpResponse object and that
used to make a request to a downstream service. The middleware that makes the request is is returned to the client. That is basically it with a bunch of other features!
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. ## Features
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! A quick list of Ocelot's capabilities for more information see the [documentation](https://ocelot.readthedocs.io/en/latest/).
## Features * Routing
* Request Aggregation
A quick list of Ocelot's capabilities for more information see the [documentation](https://ocelot.readthedocs.io/en/latest/). * Service Discovery with Consul & Eureka
* Service Fabric
* Routing * Kubernetes
* Request Aggregation * WebSockets
* Service Discovery with Consul & Eureka * Authentication
* Service Fabric * Authorisation
* Kubernetes * Rate Limiting
* WebSockets * Caching
* Authentication * Retry policies / QoS
* Authorisation * Load Balancing
* Rate Limiting * Logging / Tracing / Correlation
* Caching * Headers / Query String / Claims Transformation
* Retry policies / QoS * Custom Middleware / Delegating Handlers
* Load Balancing * Configuration / Administration REST API
* Logging / Tracing / Correlation * Platform / Cloud Agnostic
* Headers / Query String / Claims Transformation
* Custom Middleware / Delegating Handlers ## How to install
* Configuration / Administration REST API
* Platform / Cloud Agnostic 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.
## How to install Install Ocelot and it's dependencies using NuGet.
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-Package Ocelot`
Install Ocelot and it's dependencies using NuGet. Or via the .NET Core CLI:
`Install-Package Ocelot` `dotnet add package ocelot`
Or via the .NET Core CLI: All versions can be found [here](https://www.nuget.org/packages/Ocelot/)
`dotnet add package ocelot` ## Documentation
All versions can be found [here](https://www.nuget.org/packages/Ocelot/) 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.
## Documentation ## Coming up
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. You can see what we are working on [here](https://github.com/ThreeMammals/Ocelot/issues).
## Coming up ## Contributing
You can see what we are working on [here](https://github.com/ThreeMammals/Ocelot/issues). We love to receive contributions from the community so please keep them coming :)
## Contributing Pull requests, issues and commentary welcome!
We love to receive contributions from the community so please keep them coming :) 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
Pull requests, issues and commentary welcome! advice on the easiest way to do things :)
Please complete the relevant template for issues and PRs. Sometimes it's worth getting in touch with us to discuss changes 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 :)
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 :) ## Donate
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 :) If you think this project is worth supporting financially please make a contribution using the button below!
## Donate [![Support via PayPal](https://cdn.rawgit.com/twolfson/paypal-github-button/1.0.0/dist/button.svg)](https://www.paypal.me/ThreeMammals/)
If you think this project is worth supporting financially please make a contribution using the button below! ## Things that are currently annoying me
[![Support via PayPal](https://cdn.rawgit.com/twolfson/paypal-github-button/1.0.0/dist/button.svg)](https://www.paypal.me/ThreeMammals/) [![](https://codescene.io/projects/697/status.svg) Get more details at **codescene.io**.](https://codescene.io/projects/697/jobs/latest-successful/results)
## 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" #tool "nuget:?package=GitReleaseNotes"
#addin nuget:?package=Cake.Json #addin nuget:?package=Cake.Json
#addin nuget:?package=Newtonsoft.Json #addin nuget:?package=Newtonsoft.Json
#addin nuget:?package=System.Net.Http
#tool "nuget:?package=ReportGenerator" #tool "nuget:?package=ReportGenerator"
#tool "nuget:?package=coveralls.net&version=0.7.0" #tool "nuget:?package=coveralls.net&version=0.7.0"
#addin Cake.Coveralls&version=0.10.1 #addin Cake.Coveralls&version=0.10.1
// compile // compile
var compileConfig = Argument("configuration", "Release"); var compileConfig = Argument("configuration", "Release");
var slnFile = "./Ocelot.sln"; var slnFile = "./Ocelot.sln";
// build artifacts // build artifacts
@ -17,8 +19,8 @@ var artifactsDir = Directory("artifacts");
var artifactsForUnitTestsDir = artifactsDir + Directory("UnitTests"); var artifactsForUnitTestsDir = artifactsDir + Directory("UnitTests");
var unitTestAssemblies = @"./test/Ocelot.UnitTests/Ocelot.UnitTests.csproj"; var unitTestAssemblies = @"./test/Ocelot.UnitTests/Ocelot.UnitTests.csproj";
var minCodeCoverage = 80d; var minCodeCoverage = 80d;
var coverallsRepoToken = "coveralls-repo-token-ocelot"; var coverallsRepoToken = "OCELOT_COVERALLS_TOKEN";
var coverallsRepo = "https://coveralls.io/github/TomPallister/Ocelot"; var coverallsRepo = "https://coveralls.io/github/ThreeMammals/Ocelot";
// acceptance testing // acceptance testing
var artifactsForAcceptanceTestsDir = artifactsDir + Directory("AcceptanceTests"); var artifactsForAcceptanceTestsDir = artifactsDir + Directory("AcceptanceTests");
@ -37,41 +39,54 @@ var packagesDir = artifactsDir + Directory("Packages");
var releaseNotesFile = packagesDir + File("releasenotes.md"); var releaseNotesFile = packagesDir + File("releasenotes.md");
var artifactsFile = packagesDir + File("artifacts.txt"); 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 // stable releases
var tagsUrl = "https://api.github.com/repos/tompallister/ocelot/releases/tags/"; var tagsUrl = "https://api.github.com/repos/ThreeMammals/ocelot/releases/tags/";
var nugetFeedStableKey = EnvironmentVariable("nuget-apikey-stable"); var nugetFeedStableKey = EnvironmentVariable("OCELOT_NUTGET_API_KEY");
var nugetFeedStableUploadUrl = "https://www.nuget.org/api/v2/package"; var nugetFeedStableUploadUrl = "https://www.nuget.org/api/v2/package";
var nugetFeedStableSymbolsUploadUrl = "https://www.nuget.org/api/v2/package"; var nugetFeedStableSymbolsUploadUrl = "https://www.nuget.org/api/v2/package";
// internal build variables - don't change these. // internal build variables - don't change these.
var releaseTag = "";
string committedVersion = "0.0.0-dev"; string committedVersion = "0.0.0-dev";
var buildVersion = committedVersion;
GitVersion versioning = null; 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"); var target = Argument("target", "Default");
Information("target is " + target);
Information("target is " +target);
Information("Build configuration is " + compileConfig); Information("Build configuration is " + compileConfig);
Task("Default") Task("Default")
.IsDependentOn("Build"); .IsDependentOn("Build");
Task("Build") Task("Build")
.IsDependentOn("RunTests") .IsDependentOn("RunTests");
.IsDependentOn("CreatePackages");
Task("BuildAndReleaseUnstable") Task("RunTests")
.IsDependentOn("RunUnitTests")
.IsDependentOn("RunAcceptanceTests")
.IsDependentOn("RunIntegrationTests");
Task("Release")
.IsDependentOn("Build") .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") Task("Clean")
.Does(() => .Does(() =>
{ {
@ -89,11 +104,10 @@ Task("Version")
var nugetVersion = versioning.NuGetVersion; var nugetVersion = versioning.NuGetVersion;
Information("SemVer version number: " + nugetVersion); Information("SemVer version number: " + nugetVersion);
if (AppVeyor.IsRunningOnAppVeyor) if (IsRunningOnCircleCI())
{ {
Information("Persisting version number..."); Information("Persisting version number...");
PersistVersion(committedVersion, nugetVersion); PersistVersion(committedVersion, nugetVersion);
buildVersion = nugetVersion;
} }
else 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") Task("RunUnitTests")
.IsDependentOn("Compile") .IsDependentOn("Compile")
.Does(() => .Does(() =>
@ -123,71 +124,55 @@ Task("RunUnitTests")
Configuration = compileConfig, Configuration = compileConfig,
ResultsDirectory = artifactsForUnitTestsDir, ResultsDirectory = artifactsForUnitTestsDir,
ArgumentCustomization = args => args ArgumentCustomization = args => args
// this create the code coverage report
.Append("--settings test/Ocelot.UnitTests/UnitTests.runsettings") .Append("--settings test/Ocelot.UnitTests/UnitTests.runsettings")
}; };
EnsureDirectoryExists(artifactsForUnitTestsDir); EnsureDirectoryExists(artifactsForUnitTestsDir);
DotNetCoreTest(unitTestAssemblies, testSettings); 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")); var repoToken = EnvironmentVariable(coverallsRepoToken);
ReportGenerator(coverageSummaryFile, artifactsForUnitTestsDir); if (string.IsNullOrEmpty(repoToken))
if (AppVeyor.IsRunningOnAppVeyor)
{ {
var repoToken = EnvironmentVariable(coverallsRepoToken); throw new Exception(string.Format("Coveralls repo token not found. Set environment variable '{0}'", coverallsRepoToken));
if (string.IsNullOrEmpty(repoToken))
{
throw new Exception(string.Format("Coveralls repo token not found. Set environment variable '{0}'", coverallsRepoToken));
}
Information(string.Format("Uploading test coverage to {0}", coverallsRepo));
CoverallsNet(coverageSummaryFile, CoverallsNetReportType.OpenCover, new CoverallsNetSettings()
{
RepoToken = repoToken
});
}
else
{
Information("We are not running on the build server so we won't publish the coverage report to coveralls.io");
} }
var sequenceCoverage = XmlPeek(coverageSummaryFile, "//CoverageSession/Summary/@sequenceCoverage"); Information(string.Format("Uploading test coverage to {0}", coverallsRepo));
var branchCoverage = XmlPeek(coverageSummaryFile, "//CoverageSession/Summary/@branchCoverage"); CoverallsNet(coverageSummaryFile, CoverallsNetReportType.OpenCover, new CoverallsNetSettings()
Information("Sequence Coverage: " + sequenceCoverage);
if(double.Parse(sequenceCoverage) < minCodeCoverage)
{ {
var whereToCheck = !AppVeyor.IsRunningOnAppVeyor ? coverallsRepo : artifactsForUnitTestsDir; RepoToken = repoToken
throw new Exception(string.Format("Code coverage fell below the threshold of {0}%. You can find the code coverage report at {1}", minCodeCoverage, whereToCheck)); });
};
} }
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") Task("RunAcceptanceTests")
.IsDependentOn("Compile") .IsDependentOn("Compile")
.Does(() => .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 var settings = new DotNetCoreTestSettings
{ {
Configuration = compileConfig, Configuration = compileConfig,
@ -204,24 +189,6 @@ Task("RunIntegrationTests")
.IsDependentOn("Compile") .IsDependentOn("Compile")
.Does(() => .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 var settings = new DotNetCoreTestSettings
{ {
Configuration = compileConfig, Configuration = compileConfig,
@ -234,12 +201,7 @@ Task("RunIntegrationTests")
DotNetCoreTest(integrationTestAssemblies, settings); DotNetCoreTest(integrationTestAssemblies, settings);
}); });
Task("RunTests") Task("CreateArtifacts")
.IsDependentOn("RunUnitTests")
.IsDependentOn("RunAcceptanceTests")
.IsDependentOn("RunIntegrationTests");
Task("CreatePackages")
.IsDependentOn("Compile") .IsDependentOn("Compile")
.Does(() => .Does(() =>
{ {
@ -247,6 +209,7 @@ Task("CreatePackages")
CopyFiles("./src/**/Release/Ocelot.*.nupkg", packagesDir); CopyFiles("./src/**/Release/Ocelot.*.nupkg", packagesDir);
// todo fix this for docker build
//GenerateReleaseNotes(releaseNotesFile); //GenerateReleaseNotes(releaseNotesFile);
var projectFiles = GetFiles("./src/**/Release/Ocelot.*.nupkg"); var projectFiles = GetFiles("./src/**/Release/Ocelot.*.nupkg");
@ -255,6 +218,7 @@ Task("CreatePackages")
{ {
System.IO.File.AppendAllLines(artifactsFile, new[]{ System.IO.File.AppendAllLines(artifactsFile, new[]{
projectFile.GetFilename().FullPath, projectFile.GetFilename().FullPath,
// todo fix this for docker build
//"releaseNotes:releasenotes.md" //"releaseNotes:releasenotes.md"
}); });
} }
@ -269,101 +233,60 @@ Task("CreatePackages")
Information("Created package " + codePackage); Information("Created package " + codePackage);
} }
});
if (AppVeyor.IsRunningOnAppVeyor) Task("PublishGitHubRelease")
.IsDependentOn("CreateArtifacts")
.Does(() =>
{
if (IsRunningOnCircleCI())
{ {
var path = packagesDir.ToString() + @"/**/*"; var path = packagesDir.ToString() + @"/**/*";
CreateGitHubRelease();
foreach (var file in GetFiles(path)) foreach (var file in GetFiles(path))
{ {
AppVeyor.UploadArtifact(file.FullPath); UploadFileToGitHubRelease(file);
} }
}
});
Task("ReleasePackagesToUnstableFeed") CompleteGitHubRelease();
.IsDependentOn("CreatePackages")
.Does(() =>
{
if (ShouldPublishToUnstableFeed(nugetFeedUnstableBranchFilter, versioning.BranchName))
{
PublishPackages(packagesDir, artifactsFile, nugetFeedUnstableKey, nugetFeedUnstableUploadUrl, nugetFeedUnstableSymbolsUploadUrl);
} }
}); });
Task("EnsureStableReleaseRequirements") Task("EnsureStableReleaseRequirements")
.Does(() => .Does(() =>
{ {
Information("Check if stable release..."); Information("Check if stable release...");
if (!AppVeyor.IsRunningOnAppVeyor) if (!IsRunningOnCircleCI())
{ {
throw new Exception("Stable release should happen via appveyor"); throw new Exception("Stable release should happen via circleci");
}
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");
} }
Information("Release is stable..."); Information("Release is stable...");
}); });
Task("UpdateVersionInfo")
.IsDependentOn("EnsureStableReleaseRequirements")
.Does(() =>
{
releaseTag = AppVeyor.Environment.Repository.Tag.Name;
AppVeyor.UpdateBuildVersion(releaseTag);
});
Task("DownloadGitHubReleaseArtifacts") Task("DownloadGitHubReleaseArtifacts")
.IsDependentOn("UpdateVersionInfo")
.Does(() => .Does(() =>
{ {
try try
{ {
Information("DownloadGitHubReleaseArtifacts");
EnsureDirectoryExists(packagesDir); EnsureDirectoryExists(packagesDir);
Information("Directory exists..."); var releaseUrl = tagsUrl + versioning.NuGetVersion;
var releaseUrl = tagsUrl + releaseTag;
Information("Release url " + releaseUrl);
var assets_url = Newtonsoft.Json.Linq.JObject.Parse(GetResource(releaseUrl)) var assets_url = Newtonsoft.Json.Linq.JObject.Parse(GetResource(releaseUrl))
.Value<string>("assets_url"); .Value<string>("assets_url");
Information("Assets url " + assets_url);
var assets = GetResource(assets_url); var assets = GetResource(assets_url);
Information("Assets " + assets_url);
foreach(var asset in Newtonsoft.Json.JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JArray>(assets)) 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")); var file = packagesDir + File(asset.Value<string>("name"));
Information("Downloading " + file);
DownloadFile(asset.Value<string>("browser_download_url"), file); DownloadFile(asset.Value<string>("browser_download_url"), file);
} }
Information("Out of the loop...");
} }
catch(Exception exception) catch(Exception exception)
{ {
@ -372,16 +295,16 @@ Task("DownloadGitHubReleaseArtifacts")
} }
}); });
Task("ReleasePackagesToStableFeed") Task("PublishToNuget")
.IsDependentOn("DownloadGitHubReleaseArtifacts") .IsDependentOn("DownloadGitHubReleaseArtifacts")
.Does(() => .Does(() =>
{ {
PublishPackages(packagesDir, artifactsFile, nugetFeedStableKey, nugetFeedStableUploadUrl, nugetFeedStableSymbolsUploadUrl); if (IsRunningOnCircleCI())
{
PublishPackages(packagesDir, artifactsFile, nugetFeedStableKey, nugetFeedStableUploadUrl, nugetFeedStableSymbolsUploadUrl);
}
}); });
Task("Release")
.IsDependentOn("ReleasePackagesToStableFeed");
RunTarget(target); RunTarget(target);
/// Gets nuique nuget version for this commit /// 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 /// 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) private void PublishPackages(ConvertableDirectoryPath packagesDir, ConvertableFilePath artifactsFile, string feedApiKey, string codeFeedUrl, string symbolFeedUrl)
{ {
Information("PublishPackages");
var artifacts = System.IO.File var artifacts = System.IO.File
.ReadAllLines(artifactsFile) .ReadAllLines(artifactsFile)
.Distinct(); .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 /// gets the resource from the specified url
private string GetResource(string 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); return !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("CIRCLECI"));
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;
}

View File

@ -59,7 +59,10 @@ try {
# Use integers because the enumeration values for TLS 1.2 and TLS 1.1 won't # 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 # exist in .NET 4.0, even though they are addressable if .NET 4.5+ is
# installed (.NET 4.5 is an in-place upgrade). # 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 { } 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' 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 # Make sure tools folder exists
if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) { if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) {
Write-Verbose -Message "Creating tools directory..." 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. # 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 # 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? # Restore tools from NuGet?
if(-Not $SkipToolPackageRestore.IsPresent) { if(-Not $SkipToolPackageRestore.IsPresent) {
@ -163,16 +171,17 @@ if(-Not $SkipToolPackageRestore.IsPresent) {
Set-Location $TOOLS_DIR Set-Location $TOOLS_DIR
# Check for changes in packages.config and remove installed tools if true. # 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 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..." Write-Verbose -Message "Missing or changed package.config hash..."
Get-ChildItem -Exclude packages.config,nuget.exe,Cake.Bakery | Get-ChildItem -Exclude packages.config,nuget.exe,Cake.Bakery |
Remove-Item -Recurse Remove-Item -Recurse -Force
} }
Write-Verbose -Message "Restoring tools from NuGet..." 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) { if ($LASTEXITCODE -ne 0) {
Throw "An error occurred while restoring NuGet tools." Throw "An error occurred while restoring NuGet tools."
@ -181,7 +190,7 @@ if(-Not $SkipToolPackageRestore.IsPresent) {
{ {
$md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII" $md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII"
} }
Write-Verbose -Message ($NuGetOutput | out-string) Write-Verbose -Message ($NuGetOutput | Out-String)
Pop-Location Pop-Location
} }
@ -192,13 +201,13 @@ if (Test-Path $ADDINS_PACKAGES_CONFIG) {
Set-Location $ADDINS_DIR Set-Location $ADDINS_DIR
Write-Verbose -Message "Restoring addins from NuGet..." 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) { if ($LASTEXITCODE -ne 0) {
Throw "An error occurred while restoring NuGet addins." Throw "An error occurred while restoring NuGet addins."
} }
Write-Verbose -Message ($NuGetOutput | out-string) Write-Verbose -Message ($NuGetOutput | Out-String)
Pop-Location Pop-Location
} }
@ -209,13 +218,13 @@ if (Test-Path $MODULES_PACKAGES_CONFIG) {
Set-Location $MODULES_DIR Set-Location $MODULES_DIR
Write-Verbose -Message "Restoring modules from NuGet..." 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) { if ($LASTEXITCODE -ne 0) {
Throw "An error occurred while restoring NuGet modules." Throw "An error occurred while restoring NuGet modules."
} }
Write-Verbose -Message ($NuGetOutput | out-string) Write-Verbose -Message ($NuGetOutput | Out-String)
Pop-Location Pop-Location
} }
@ -225,11 +234,16 @@ if (!(Test-Path $CAKE_EXE)) {
Throw "Could not find Cake.exe at $CAKE_EXE" Throw "Could not find Cake.exe at $CAKE_EXE"
} }
$CAKE_EXE_INVOCATION = if ($IsLinux -or $IsMacOS) {
"mono `"$CAKE_EXE`""
} else {
"`"$CAKE_EXE`""
}
# Build an array (not a string) of Cake arguments to be joined later
# Build Cake arguments $cakeArguments = @()
$cakeArguments = @("$Script"); if ($Script) { $cakeArguments += "`"$Script`"" }
if ($Target) { $cakeArguments += "-target=$Target" } if ($Target) { $cakeArguments += "-target=`"$Target`"" }
if ($Configuration) { $cakeArguments += "-configuration=$Configuration" } if ($Configuration) { $cakeArguments += "-configuration=$Configuration" }
if ($Verbosity) { $cakeArguments += "-verbosity=$Verbosity" } if ($Verbosity) { $cakeArguments += "-verbosity=$Verbosity" }
if ($ShowDescription) { $cakeArguments += "-showdescription" } if ($ShowDescription) { $cakeArguments += "-showdescription" }
@ -238,5 +252,5 @@ $cakeArguments += $ScriptArgs
# Start Cake # Start Cake
Write-Host "Running build script..." Write-Host "Running build script..."
&$CAKE_EXE $cakeArguments Invoke-Expression "& $CAKE_EXE_INVOCATION $($cakeArguments -join " ")"
exit $LASTEXITCODE 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 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 VS2019 with the latest .NET Core SDK.
* Alternatively you can build the project in VS2017 with the latest .NET Core SDK.

View File

@ -1,4 +1,4 @@
Overview 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 Release process
=============== ===============
Ocelot uses the following process to accept work into the NuGet packages. * 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.
1. User creates an issue or picks up an existing issue in GitHub.
Ocelot uses the following process to accept work into the NuGet packages.
2. User creates a fork and branches from this (unless a member of core team, they can just create a branch on the main repo) e.g. feat/xxx, fix/xxx etc. It doesn't really matter what the xxx is. It might make sense to use the issue number and maybe a short description. I don't care as long as it has (feat, fix, refactor)/xxx :)
1. User creates an issue or picks up an existing issue in GitHub.
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.
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 :)
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? 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.
- 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? 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.
In order for a PR to be merged the following must have occured. - Have I covered all my changes with tests at unit and acceptance level?
- All new code is covered by unit tests. - Have I updated any documentation that my changes may have affected?
- All new code has at least 1 acceptance test covering the happy path. - Does my feature make sense, have I checked all of Ocelot's other features to make sure it doesn't already exist?
- Builds for Windows, Mac and Linux must have passed. In order for a PR to be merged the following must have occured.
- Builds for Windows, Mac and Linux must not have slowed down dramatically. - All new code is covered by unit tests.
- The main Ocelot package must not have taken on any non MS dependencies. - All new code has at least 1 acceptance test covering the happy path.
- Tests must have passed.
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. - Build must not have slowed down dramatically.
- The main Ocelot package must not have taken on any non MS dependencies.
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.
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.
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.
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>`_.
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.
Notes
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 & 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.
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.

View File

@ -1,54 +1,54 @@
Caching Caching
======= =======
Ocelot supports some very rudimentary caching at the moment provider by Ocelot supports some very rudimentary caching at the moment provider by
the `CacheManager <https://github.com/MichaCo/CacheManager>`_ project. This is an amazing project 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 that is solving a lot of caching problems. I would reccomend using this package to
cache with Ocelot. cache with Ocelot.
The following example shows how to add CacheManager to Ocelot so that you can do output caching. 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. First of all add the following NuGet package.
``Install-Package Ocelot.Cache.CacheManager`` ``Install-Package Ocelot.Cache.CacheManager``
This will give you access to the Ocelot cache manager extension methods. 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.. The second thing you need to do something like the following to your ConfigureServices..
.. code-block:: csharp .. code-block:: csharp
s.AddOcelot() s.AddOcelot()
.AddCacheManager(x => .AddCacheManager(x =>
{ {
x.WithDictionaryHandle(); x.WithDictionaryHandle();
}) })
Finally in order to use caching on a route in your ReRoute configuration add this setting. Finally in order to use caching on a route in your ReRoute configuration add this setting.
.. code-block:: json .. code-block:: json
"FileCacheOptions": { "TtlSeconds": 15, "Region": "somename" } "FileCacheOptions": { "TtlSeconds": 15, "Region": "somename" }
In this example ttl seconds is set to 15 which means the cache will expire after 15 seconds. 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 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 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. the CacheManager package and just pass them in.
Anyway Ocelot currently supports caching on the URL of the downstream service 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 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. by calling Ocelot's administration API.
Your own caching Your own caching
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
If you want to add your own caching method implement the following interfaces and register them in DI 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>()`` e.g. ``services.AddSingleton<IOcelotCache<CachedResponse>, MyCache>()``
``IOcelotCache<CachedResponse>`` this is for output caching. ``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. ``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.. 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 Configuration
============ ============
An example configuration can be found `here <https://github.com/ThreeMammals/Ocelot/blob/develop/test/Ocelot.ManualTest/ocelot.json>`_. 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. 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 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 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. if you don't want to manage lots of ReRoute specific settings.
.. code-block:: json .. code-block:: json
{ {
"ReRoutes": [], "ReRoutes": [],
"GlobalConfiguration": {} "GlobalConfiguration": {}
} }
Here is an example ReRoute configuration, You don't need to set all of these things but this is everything that is available at the moment: Here is an example 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 .. code-block:: json
{ {
"DownstreamPathTemplate": "/", "DownstreamPathTemplate": "/",
"UpstreamPathTemplate": "/", "UpstreamPathTemplate": "/",
"UpstreamHttpMethod": [ "UpstreamHttpMethod": [
"Get" "Get"
], ],
"AddHeadersToRequest": {}, "AddHeadersToRequest": {},
"AddClaimsToRequest": {}, "AddClaimsToRequest": {},
"RouteClaimsRequirement": {}, "RouteClaimsRequirement": {},
"AddQueriesToRequest": {}, "AddQueriesToRequest": {},
"RequestIdKey": "", "RequestIdKey": "",
"FileCacheOptions": { "FileCacheOptions": {
"TtlSeconds": 0, "TtlSeconds": 0,
"Region": "" "Region": ""
}, },
"ReRouteIsCaseSensitive": false, "ReRouteIsCaseSensitive": false,
"ServiceName": "", "ServiceName": "",
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHostAndPorts": [ "DownstreamHostAndPorts": [
{ {
"Host": "localhost", "Host": "localhost",
"Port": 51876, "Port": 51876,
} }
], ],
"QoSOptions": { "QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 0, "ExceptionsAllowedBeforeBreaking": 0,
"DurationOfBreak": 0, "DurationOfBreak": 0,
"TimeoutValue": 0 "TimeoutValue": 0
}, },
"LoadBalancer": "", "LoadBalancer": "",
"RateLimitOptions": { "RateLimitOptions": {
"ClientWhitelist": [], "ClientWhitelist": [],
"EnableRateLimiting": false, "EnableRateLimiting": false,
"Period": "", "Period": "",
"PeriodTimespan": 0, "PeriodTimespan": 0,
"Limit": 0 "Limit": 0
}, },
"AuthenticationOptions": { "AuthenticationOptions": {
"AuthenticationProviderKey": "", "AuthenticationProviderKey": "",
"AllowedScopes": [] "AllowedScopes": []
}, },
"HttpHandlerOptions": { "HttpHandlerOptions": {
"AllowAutoRedirect": true, "AllowAutoRedirect": true,
"UseCookieContainer": true, "UseCookieContainer": true,
"UseTracing": true "UseTracing": true,
}, "MaxConnectionsPerServer": 100
"DangerousAcceptAnyServerCertificateValidator": false },
} "DangerousAcceptAnyServerCertificateValidator": false
}
More information on how to use these options is below..
More information on how to use these options is below..
Multiple environments
^^^^^^^^^^^^^^^^^^^^^ 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 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
.. code-block:: csharp
.ConfigureAppConfiguration((hostingContext, config) =>
{ .ConfigureAppConfiguration((hostingContext, config) =>
config {
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath) config
.AddJsonFile("appsettings.json", true, true) .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true) .AddJsonFile("appsettings.json", true, true)
.AddJsonFile("ocelot.json") .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
.AddJsonFile($"configuration.{hostingContext.HostingEnvironment.EnvironmentName}.json") .AddJsonFile("ocelot.json")
.AddEnvironmentVariables(); .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.
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>`_.
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
^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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.
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.
Instead of adding the configuration directly e.g. AddJsonFile("ocelot.json") you can call AddOcelot() like below.
.. code-block:: csharp
.. code-block:: csharp
.ConfigureAppConfiguration((hostingContext, config) =>
{ .ConfigureAppConfiguration((hostingContext, config) =>
config {
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath) config
.AddJsonFile("appsettings.json", true, true) .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true) .AddJsonFile("appsettings.json", true, true)
.AddOcelot(hostingContext.HostingEnvironment) .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
.AddEnvironmentVariables(); .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.
In this scenario Ocelot will look for any files that match the pattern (?i)ocelot.([a-zA-Z0-9]*).json and then merge these together. If you want to set the GlobalConfiguration property you must have a file called ocelot.global.json.
The way Ocelot merges the files is basically load them, loop over them, add any ReRoutes, add any AggregateReRoutes and if the file is called ocelot.global.json add the GlobalConfiguration aswell as any ReRoutes or AggregateReRoutes. Ocelot will then save the merged configuration to a file called ocelot.json and this will be used as the source of truth while ocelot is running.
The way Ocelot merges the files is basically load them, loop over them, add any 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.
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.
You can also give Ocelot a specific path to look in for the configuration files like below.
.. code-block:: csharp
.. code-block:: csharp
.ConfigureAppConfiguration((hostingContext, config) =>
{ .ConfigureAppConfiguration((hostingContext, config) =>
config {
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath) config
.AddJsonFile("appsettings.json", true, true) .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true) .AddJsonFile("appsettings.json", true, true)
.AddOcelot("/foo/bar", hostingContext.HostingEnvironment) .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
.AddEnvironmentVariables(); .AddOcelot("/foo/bar", hostingContext.HostingEnvironment)
}) .AddEnvironmentVariables();
})
Ocelot needs the HostingEnvironment so it knows to exclude anything environment specific from the algorithm.
Ocelot needs the HostingEnvironment so it knows to exclude anything environment specific from the algorithm.
Store configuration in consul
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Store configuration in consul
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The first thing you need to do is install the NuGet package that provides Consul support in Ocelot.
The first thing you need to do is install the NuGet package that provides Consul support in Ocelot.
``Install-Package Ocelot.Provider.Consul``
``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.
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
.. code-block:: csharp
services
.AddOcelot() services
.AddConsul() .AddOcelot()
.AddConfigStoredInConsul(); .AddConsul()
.AddConfigStoredInConsul();
You also need to add the following to your ocelot.json. This is how Ocelot
finds your Consul agent and interacts to load and store the configuration from Consul. You also need to add the following to your ocelot.json. This is how Ocelot
finds your Consul agent and interacts to load and store the configuration from Consul.
.. code-block:: json
.. code-block:: json
"GlobalConfiguration": {
"ServiceDiscoveryProvider": { "GlobalConfiguration": {
"Host": "localhost", "ServiceDiscoveryProvider": {
"Port": 9500 "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. 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.
This feature has a 3 second ttl cache before making a new request to your local consul agent.
Reload JSON config on change
^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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. 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
.. code-block:: json
config.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);
config.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);
Configuration Key
----------------- 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.
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
.. code-block:: json
"GlobalConfiguration": {
"ServiceDiscoveryProvider": { "GlobalConfiguration": {
"Host": "localhost", "ServiceDiscoveryProvider": {
"Port": 9500, "Host": "localhost",
"ConfigurationKey": "Oceolot_A" "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.
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.
If you do not set the ConfigurationKey Ocelot will use the string InternalConfiguration as the key.
Follow Redirects / Use CookieContainer
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Follow Redirects / Use CookieContainer
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Use HttpHandlerOptions in ReRoute configuration to set up HttpHandler behavior:
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. 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 2. UseCookieContainer is a value that indicates whether the handler uses the CookieContainer
that if you are using the CookieContainer Ocelot caches the HttpClient for each downstream service. This means that all requests property to store server cookies and uses these cookies when sending requests. The default value is false. Please note
to that DownstreamService will share the same cookies. `Issue 274 <https://github.com/ThreeMammals/Ocelot/issues/274>`_ was created because a user that if you are using the CookieContainer Ocelot caches the HttpClient for each downstream service. This means that all requests
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 to that DownstreamService will share the same cookies. `Issue 274 <https://github.com/ThreeMammals/Ocelot/issues/274>`_ was created because a user
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 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
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 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
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! 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
^^^^^^^^^^ SSL Errors
^^^^^^^^^^
If you want to ignore SSL warnings / errors set the following in your ReRoute config.
If you want to ignore SSL warnings / errors set the following in your ReRoute config.
.. code-block:: json
.. code-block:: json
"DangerousAcceptAnyServerCertificateValidator": true
"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.
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 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 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. 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>`_. 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. 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 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! you enough instruction on how to do this!
Good luck and have fun :> 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 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. 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 Dynamic Routing
^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^

View File

@ -4,7 +4,7 @@ Getting Started
Ocelot is designed to work with .NET Core only and is currently 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. 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** **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 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. 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 .. code-block:: csharp
using System.IO;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Ocelot.DependencyInjection; using Ocelot.DependencyInjection;
using Ocelot.Middleware; 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() .UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory()) .UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) => .ConfigureAppConfiguration((hostingContext, config) =>
@ -74,9 +104,11 @@ AddOcelot() (adds ocelot services), UseOcelot().Wait() (sets up all the Ocelot m
app.UseOcelot().Wait(); app.UseOcelot().Wait();
}) })
.Build() .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. **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"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Folder Include="wwwroot\"/> <Folder Include="wwwroot\"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App"/> <PackageReference Include="Microsoft.AspNetCore.App"/>
<PackageReference Include="Ocelot" Version="12.0.1"/> <PackageReference Include="Ocelot" Version="12.0.1"/>
<PackageReference Include="Ocelot.Administration" Version="0.1.0"/> <PackageReference Include="Ocelot.Administration" Version="0.1.0"/>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,24 +1,24 @@
version: "3.4" version: "3.4"
services: services:
tests: tests:
build: build:
context: . context: .
target: builder target: builder
volumes: volumes:
- type: bind - type: bind
source: . source: .
target: /results target: /results
command: test --logger:trx -r /results command: test --logger:trx -r /results
benchmarks: benchmarks:
build: build:
context: . context: .
target: builder target: builder
args: args:
build_configuration: Release build_configuration: Release
command: run -c Release --project test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj 0 1 2 3 4 command: run -c Release --project test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj 0 1 2 3 4
manual-test: manual-test:
build: . build: .
ports: [ "5000:80" ] ports: [ "5000:80" ]

View File

@ -1,48 +1,48 @@
#This is the base image used for any ran images #This is the base image used for any ran images
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-stretch-slim AS base FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-stretch-slim AS base
WORKDIR /app WORKDIR /app
EXPOSE 80 EXPOSE 80
#This image is used to build the source for the runnable app #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: #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 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 #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 FROM mcr.microsoft.com/dotnet/core/sdk:2.2-stretch AS builder
WORKDIR /build WORKDIR /build
#First we add only the project files so that we can cache nuget packages with dotnet restore #First we add only the project files so that we can cache nuget packages with dotnet restore
COPY Ocelot.sln Ocelot.sln COPY Ocelot.sln Ocelot.sln
COPY src/Ocelot/Ocelot.csproj src/Ocelot/Ocelot.csproj 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.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.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.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.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.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.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.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 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.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.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.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.UnitTests/Ocelot.UnitTests.csproj test/Ocelot.UnitTests/Ocelot.UnitTests.csproj
COPY test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj COPY test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj
RUN dotnet restore 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 #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 codeanalysis.ruleset codeanalysis.ruleset
COPY src src COPY src src
COPY test test COPY test test
ARG build_configuration=Debug ARG build_configuration=Debug
RUN dotnet build --no-restore -c ${build_configuration} RUN dotnet build --no-restore -c ${build_configuration}
ENTRYPOINT ["dotnet"] ENTRYPOINT ["dotnet"]
#This is just for holding the published manual tests... #This is just for holding the published manual tests...
FROM builder AS manual-test-publish FROM builder AS manual-test-publish
ARG build_configuration=Debug ARG build_configuration=Debug
RUN dotnet publish --no-build -c ${build_configuration} -o /app test/Ocelot.ManualTest RUN dotnet publish --no-build -c ${build_configuration} -o /app test/Ocelot.ManualTest
#Run manual tests! This is the default run option. #Run manual tests! This is the default run option.
#docker build -t ocelot-manual-test . && docker run --net host ocelot-manual-test #docker build -t ocelot-manual-test . && docker run --net host ocelot-manual-test
FROM base AS manual-test FROM base AS manual-test
ENV ASPNETCORE_ENVIRONMENT=Development ENV ASPNETCORE_ENVIRONMENT=Development
COPY --from=manual-test-publish /app . COPY --from=manual-test-publish /app .
ENTRYPOINT ["dotnet", "Ocelot.ManualTest.dll"] 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"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Folder Include="wwwroot\" /> <Folder Include="wwwroot\" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="ocelot.json;appsettings.json"> <None Update="ocelot.json;appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.2.5" /> <PackageReference Include="Ocelot" Version="13.5.1" />
<PackageReference Include="Ocelot" Version="13.5.1" /> <PackageReference Include="Ocelot.Provider.Eureka" Version="13.5.1" />
<PackageReference Include="Ocelot.Provider.Eureka" Version="13.5.1" /> <PackageReference Include="Ocelot.Provider.Polly" Version="13.5.1" />
<PackageReference Include="Ocelot.Provider.Polly" Version="13.5.1" /> </ItemGroup>
</ItemGroup>
</Project>
</Project>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -26,7 +26,7 @@
if (instances != null && instances.Any()) 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); return Task.FromResult(services);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
namespace Ocelot.Configuration.Builder namespace Ocelot.Configuration.Builder
{ {
@ -7,6 +8,7 @@ namespace Ocelot.Configuration.Builder
private bool _enableRateLimiting; private bool _enableRateLimiting;
private string _clientIdHeader; private string _clientIdHeader;
private List<string> _clientWhitelist; private List<string> _clientWhitelist;
private Func<List<string>> _getClientWhitelist;
private bool _disableRateLimitHeaders; private bool _disableRateLimitHeaders;
private string _quotaExceededMessage; private string _quotaExceededMessage;
private string _rateLimitCounterPrefix; private string _rateLimitCounterPrefix;
@ -19,15 +21,15 @@ namespace Ocelot.Configuration.Builder
return this; return this;
} }
public RateLimitOptionsBuilder WithClientIdHeader(string clientIdheader) public RateLimitOptionsBuilder WithClientIdHeader(string clientIdHeader)
{ {
_clientIdHeader = clientIdheader; _clientIdHeader = clientIdHeader;
return this; return this;
} }
public RateLimitOptionsBuilder WithClientWhiteList(List<string> clientWhitelist) public RateLimitOptionsBuilder WithClientWhiteList(Func<List<string>> getClientWhitelist)
{ {
_clientWhitelist = clientWhitelist; _getClientWhitelist = getClientWhitelist;
return this; return this;
} }
@ -63,9 +65,9 @@ namespace Ocelot.Configuration.Builder
public RateLimitOptions Build() public RateLimitOptions Build()
{ {
return new RateLimitOptions(_enableRateLimiting, _clientIdHeader, _clientWhitelist, return new RateLimitOptions(_enableRateLimiting, _clientIdHeader, _getClientWhitelist,
_disableRateLimitHeaders, _quotaExceededMessage, _rateLimitCounterPrefix, _disableRateLimitHeaders, _quotaExceededMessage, _rateLimitCounterPrefix,
_rateLimitRule, _httpStatusCode); _rateLimitRule, _httpStatusCode);
} }
} }
} }

View File

@ -18,8 +18,11 @@
{ {
var useTracing = _tracer != null && options.UseTracing; 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, 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() return new RateLimitOptionsBuilder()
.WithClientIdHeader(globalConfiguration.RateLimitOptions.ClientIdHeader) .WithClientIdHeader(globalConfiguration.RateLimitOptions.ClientIdHeader)
.WithClientWhiteList(fileRateLimitRule.ClientWhitelist) .WithClientWhiteList(() => fileRateLimitRule.ClientWhitelist)
.WithDisableRateLimitHeaders(globalConfiguration.RateLimitOptions.DisableRateLimitHeaders) .WithDisableRateLimitHeaders(globalConfiguration.RateLimitOptions.DisableRateLimitHeaders)
.WithEnableRateLimiting(fileRateLimitRule.EnableRateLimiting) .WithEnableRateLimiting(fileRateLimitRule.EnableRateLimiting)
.WithHttpStatusCode(globalConfiguration.RateLimitOptions.HttpStatusCode) .WithHttpStatusCode(globalConfiguration.RateLimitOptions.HttpStatusCode)

View File

@ -6,7 +6,8 @@
{ {
AllowAutoRedirect = false; AllowAutoRedirect = false;
UseCookieContainer = false; UseCookieContainer = false;
UseProxy = true; UseProxy = true;
MaxConnectionsPerServer = int.MaxValue;
} }
public bool AllowAutoRedirect { get; set; } public bool AllowAutoRedirect { get; set; }
@ -15,6 +16,8 @@
public bool UseTracing { get; set; } 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> /// </summary>
public class HttpHandlerOptions public class HttpHandlerOptions
{ {
public HttpHandlerOptions(bool allowAutoRedirect, bool useCookieContainer, bool useTracing, bool useProxy) public HttpHandlerOptions(bool allowAutoRedirect, bool useCookieContainer, bool useTracing, bool useProxy, int maxConnectionsPerServer)
{ {
AllowAutoRedirect = allowAutoRedirect; AllowAutoRedirect = allowAutoRedirect;
UseCookieContainer = useCookieContainer; UseCookieContainer = useCookieContainer;
UseTracing = useTracing; UseTracing = useTracing;
UseProxy = useProxy; UseProxy = useProxy;
MaxConnectionsPerServer = maxConnectionsPerServer;
} }
/// <summary> /// <summary>
/// Specify if auto redirect is enabled /// Specify if auto redirect is enabled
/// </summary> /// </summary>
public bool AllowAutoRedirect { get; private set; } /// <value>AllowAutoRedirect</value>
public bool AllowAutoRedirect { get; private set; }
/// <summary> /// <summary>
/// Specify is handler has to use a cookie container /// Specify is handler has to use a cookie container
/// </summary> /// </summary>
/// <value>UseCookieContainer</value>
public bool UseCookieContainer { get; private set; } public bool UseCookieContainer { get; private set; }
/// <summary> /// <summary>
/// Specify is handler has to use a opentracing /// Specify is handler has to use a opentracing
/// </summary> /// </summary>
/// <value>UseTracing</value>
public bool UseTracing { get; private set; } public bool UseTracing { get; private set; }
/// <summary> /// <summary>
/// Specify if handler has to use a proxy /// Specify if handler has to use a proxy
/// </summary> /// </summary>
public bool UseProxy { get; private set; } /// <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 _useCookieContainer;
private bool _useTracing; private bool _useTracing;
private bool _useProxy; private bool _useProxy;
private int _maxConnectionPerServer;
public HttpHandlerOptionsBuilder WithAllowAutoRedirect(bool input) public HttpHandlerOptionsBuilder WithAllowAutoRedirect(bool input)
{ {
@ -30,10 +31,16 @@
_useProxy = useProxy; _useProxy = useProxy;
return this; return this;
} }
public HttpHandlerOptionsBuilder WithUseMaxConnectionPerServer(int maxConnectionPerServer)
{
_maxConnectionPerServer = maxConnectionPerServer;
return this;
}
public HttpHandlerOptions Build() 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 namespace Ocelot.Configuration
{ {
@ -7,12 +8,14 @@ namespace Ocelot.Configuration
/// </summary> /// </summary>
public class RateLimitOptions 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) string quotaExceededMessage, string rateLimitCounterPrefix, RateLimitRule rateLimitRule, int httpStatusCode)
{ {
EnableRateLimiting = enbleRateLimiting; EnableRateLimiting = enableRateLimiting;
ClientIdHeader = clientIdHeader; ClientIdHeader = clientIdHeader;
ClientWhitelist = clientWhitelist ?? new List<string>(); _getClientWhitelist = getClientWhitelist;
DisableRateLimitHeaders = disableRateLimitHeaders; DisableRateLimitHeaders = disableRateLimitHeaders;
QuotaExceededMessage = quotaExceededMessage; QuotaExceededMessage = quotaExceededMessage;
RateLimitCounterPrefix = rateLimitCounterPrefix; RateLimitCounterPrefix = rateLimitCounterPrefix;
@ -22,18 +25,21 @@ namespace Ocelot.Configuration
public RateLimitRule RateLimitRule { get; private set; } 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> /// <summary>
/// Gets or sets the HTTP header that holds the client identifier, by default is X-ClientId /// Gets or sets the HTTP header that holds the client identifier, by default is X-ClientId
/// </summary> /// </summary>
public string ClientIdHeader { get; private set; } public string ClientIdHeader { get; private set; }
/// <summary> /// <summary>
/// Gets or sets the HTTP Status code returned when rate limiting occurs, by default value is set to 429 (Too Many Requests) /// Gets or sets the HTTP Status code returned when rate limiting occurs, by default value is set to 429 (Too Many Requests)
/// </summary> /// </summary>
public int HttpStatusCode { get; private set; } public int HttpStatusCode { get; private set; }
/// <summary> /// <summary>
/// Gets or sets a value that will be used as a formatter for the QuotaExceeded response message. /// Gets or sets a value that will be used as a formatter for the QuotaExceeded response message.
/// If none specified the default will be: /// If none specified the default will be:
@ -44,8 +50,8 @@ namespace Ocelot.Configuration
/// <summary> /// <summary>
/// Gets or sets the counter prefix, used to compose the rate limit counter cache key /// Gets or sets the counter prefix, used to compose the rate limit counter cache key
/// </summary> /// </summary>
public string RateLimitCounterPrefix { get; private set; } public string RateLimitCounterPrefix { get; private set; }
/// <summary> /// <summary>
/// Enables endpoint rate limiting based URL path and HTTP verb /// Enables endpoint rate limiting based URL path and HTTP verb
/// </summary> /// </summary>
@ -56,4 +62,4 @@ namespace Ocelot.Configuration
/// </summary> /// </summary>
public bool DisableRateLimitHeaders { get; private set; } public bool DisableRateLimitHeaders { get; private set; }
} }
} }

View File

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

View File

@ -9,6 +9,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
public class FileConfigurationFluentValidator : AbstractValidator<FileConfiguration>, IConfigurationValidator public class FileConfigurationFluentValidator : AbstractValidator<FileConfiguration>, IConfigurationValidator
@ -35,6 +36,10 @@
.Must((config, reRoute) => HaveServiceDiscoveryProviderRegistered(reRoute, config.GlobalConfiguration.ServiceDiscoveryProvider)) .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()?"); .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) RuleFor(configuration => configuration.GlobalConfiguration.ServiceDiscoveryProvider)
.Must(HaveServiceDiscoveryProviderRegistered) .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()?"); .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; 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, private static bool DoesNotContainReRoutesWithSpecificRequestIdKeys(FileAggregateReRoute fileAggregateReRoute,
List<FileReRoute> reRoutes) List<FileReRoute> reRoutes)
{ {

View File

@ -37,7 +37,10 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
return; return;
} }
context.DownstreamRequest.Scheme = context.DownstreamReRoute.DownstreamScheme; if (!string.IsNullOrEmpty(context.DownstreamReRoute.DownstreamScheme))
{
context.DownstreamRequest.Scheme = context.DownstreamReRoute.DownstreamScheme;
}
if (ServiceFabricRequest(context)) if (ServiceFabricRequest(context))
{ {

View File

@ -45,6 +45,11 @@ namespace Ocelot.LoadBalancer.Middleware
context.DownstreamRequest.Port = hostAndPort.Data.DownstreamPort; context.DownstreamRequest.Port = hostAndPort.Data.DownstreamPort;
} }
if (!string.IsNullOrEmpty(hostAndPort.Data.Scheme))
{
context.DownstreamRequest.Scheme = hostAndPort.Data.Scheme;
}
try try
{ {
await _next.Invoke(context); await _next.Invoke(context);

View File

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

View File

@ -90,7 +90,9 @@ namespace Ocelot.Requester
{ {
AllowAutoRedirect = context.DownstreamReRoute.HttpHandlerOptions.AllowAutoRedirect, AllowAutoRedirect = context.DownstreamReRoute.HttpHandlerOptions.AllowAutoRedirect,
UseCookies = context.DownstreamReRoute.HttpHandlerOptions.UseCookieContainer, 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, AllowAutoRedirect = context.DownstreamReRoute.HttpHandlerOptions.AllowAutoRedirect,
UseCookies = context.DownstreamReRoute.HttpHandlerOptions.UseCookieContainer, UseCookies = context.DownstreamReRoute.HttpHandlerOptions.UseCookieContainer,
UseProxy = context.DownstreamReRoute.HttpHandlerOptions.UseProxy, UseProxy = context.DownstreamReRoute.HttpHandlerOptions.UseProxy,
MaxConnectionsPerServer = context.DownstreamReRoute.HttpHandlerOptions.MaxConnectionsPerServer,
CookieContainer = new CookieContainer() CookieContainer = new CookieContainer()
}; };
} }

View File

@ -1,6 +1,6 @@
namespace Ocelot.Requester namespace Ocelot.Requester
{ {
using Errors; using Ocelot.Errors;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -25,7 +25,7 @@ namespace Ocelot.Requester
return _mappers[type](exception); return _mappers[type](exception);
} }
if (type == typeof(OperationCanceledException)) if (type == typeof(OperationCanceledException) || type.IsSubclassOf(typeof(OperationCanceledException)))
{ {
return new RequestCanceledError(exception.Message); return new RequestCanceledError(exception.Message);
} }

View File

@ -1,6 +1,9 @@
using System.Net;
using System.Net.Http;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.Middleware; using Ocelot.Middleware;
using System.Threading.Tasks; using System.Threading.Tasks;
using Ocelot.Responses;
namespace Ocelot.Requester.Middleware namespace Ocelot.Requester.Middleware
{ {
@ -22,6 +25,8 @@ namespace Ocelot.Requester.Middleware
{ {
var response = await _requester.GetResponse(context); var response = await _requester.GetResponse(context);
CreateLogBasedOnResponse(response);
if (response.IsError) if (response.IsError)
{ {
Logger.LogDebug("IHttpRequester returned an error, setting pipeline error"); Logger.LogDebug("IHttpRequester returned an error, setting pipeline error");
@ -36,5 +41,19 @@ namespace Ocelot.Requester.Middleware
await _next.Invoke(context); 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) 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); services.Add(service);
} }

View File

@ -8,8 +8,13 @@
DownstreamPort = downstreamPort; DownstreamPort = downstreamPort;
} }
public ServiceHostAndPort(string downstreamHost, int downstreamPort, string scheme)
: this(downstreamHost, downstreamPort) => Scheme = scheme;
public string DownstreamHost { get; } 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 namespace Ocelot.AcceptanceTests
{ {
using Configuration.File; using Configuration.File;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net; using System.Net;
using System.Threading; using System.Threading;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
public class CachingTests : IDisposable public class CachingTests : IDisposable
{ {
private readonly Steps _steps; private readonly Steps _steps;
private readonly ServiceHandler _serviceHandler; private readonly ServiceHandler _serviceHandler;
public CachingTests() public CachingTests()
{ {
_serviceHandler = new ServiceHandler(); _serviceHandler = new ServiceHandler();
_steps = new Steps(); _steps = new Steps();
} }
[Fact] [Fact]
public void should_return_cached_response() public void should_return_cached_response()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamHostAndPorts = new List<FileHostAndPort>
{ {
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51899, Port = 57899,
} }
}, },
DownstreamScheme = "http", DownstreamScheme = "http",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
FileCacheOptions = new FileCacheOptions FileCacheOptions = new FileCacheOptions
{ {
TtlSeconds = 100 TtlSeconds = 100
} }
} }
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51899", 200, "Hello from Laura", null, null)) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:57899", 200, "Hello from Laura", null, null))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.Given(x => x.GivenTheServiceNowReturns("http://localhost:51899", 200, "Hello from Tom")) .Given(x => x.GivenTheServiceNowReturns("http://localhost:57899", 200, "Hello from Tom"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.And(x => _steps.ThenTheContentLengthIs(16)) .And(x => _steps.ThenTheContentLengthIs(16))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void should_return_cached_response_with_expires_header() public void should_return_cached_response_with_expires_header()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamHostAndPorts = new List<FileHostAndPort>
{ {
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 52839, Port = 52839,
} }
}, },
DownstreamScheme = "http", DownstreamScheme = "http",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
FileCacheOptions = new FileCacheOptions FileCacheOptions = new FileCacheOptions
{ {
TtlSeconds = 100 TtlSeconds = 100
} }
} }
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:52839", 200, "Hello from Laura", "Expires", "-1")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:52839", 200, "Hello from Laura", "Expires", "-1"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.Given(x => x.GivenTheServiceNowReturns("http://localhost:52839", 200, "Hello from Tom")) .Given(x => x.GivenTheServiceNowReturns("http://localhost:52839", 200, "Hello from Tom"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.And(x => _steps.ThenTheContentLengthIs(16)) .And(x => _steps.ThenTheContentLengthIs(16))
.And(x => _steps.ThenTheResponseBodyHeaderIs("Expires", "-1")) .And(x => _steps.ThenTheResponseBodyHeaderIs("Expires", "-1"))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void should_return_cached_response_when_using_jsonserialized_cache() public void should_return_cached_response_when_using_jsonserialized_cache()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamHostAndPorts = new List<FileHostAndPort>
{ {
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51899, Port = 57879,
} }
}, },
DownstreamScheme = "http", DownstreamScheme = "http",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
FileCacheOptions = new FileCacheOptions FileCacheOptions = new FileCacheOptions
{ {
TtlSeconds = 100 TtlSeconds = 100
} }
} }
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51899", 200, "Hello from Laura", null, null)) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:57879", 200, "Hello from Laura", null, null))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningUsingJsonSerializedCache()) .And(x => _steps.GivenOcelotIsRunningUsingJsonSerializedCache())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.Given(x => x.GivenTheServiceNowReturns("http://localhost:51899", 200, "Hello from Tom")) .Given(x => x.GivenTheServiceNowReturns("http://localhost:57879", 200, "Hello from Tom"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void should_not_return_cached_response_as_ttl_expires() public void should_not_return_cached_response_as_ttl_expires()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamHostAndPorts = new List<FileHostAndPort>
{ {
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51899, Port = 57873,
} }
}, },
DownstreamScheme = "http", DownstreamScheme = "http",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
FileCacheOptions = new FileCacheOptions FileCacheOptions = new FileCacheOptions
{ {
TtlSeconds = 1 TtlSeconds = 1
} }
} }
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51899", 200, "Hello from Laura", null, null)) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:57873", 200, "Hello from Laura", null, null))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.Given(x => x.GivenTheServiceNowReturns("http://localhost:51899", 200, "Hello from Tom")) .Given(x => x.GivenTheServiceNowReturns("http://localhost:57873", 200, "Hello from Tom"))
.And(x => x.GivenTheCacheExpires()) .And(x => x.GivenTheCacheExpires())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Tom")) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Tom"))
.BDDfy(); .BDDfy();
} }
private void GivenTheCacheExpires() private void GivenTheCacheExpires()
{ {
Thread.Sleep(1000); Thread.Sleep(1000);
} }
private void GivenTheServiceNowReturns(string url, int statusCode, string responseBody) private void GivenTheServiceNowReturns(string url, int statusCode, string responseBody)
{ {
_serviceHandler.Dispose(); _serviceHandler.Dispose();
GivenThereIsAServiceRunningOn(url, statusCode, responseBody, null, null); GivenThereIsAServiceRunningOn(url, statusCode, responseBody, null, null);
} }
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody, string key, string value) private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody, string key, string value)
{ {
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context => _serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
{ {
if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(key)) if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(key))
{ {
context.Response.Headers.Add(key, value); context.Response.Headers.Add(key, value);
} }
context.Response.StatusCode = statusCode; context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody); await context.Response.WriteAsync(responseBody);
}); });
} }
public void Dispose() public void Dispose()
{ {
_serviceHandler?.Dispose(); _serviceHandler?.Dispose();
_steps.Dispose(); _steps.Dispose();
} }
} }
} }

View File

@ -39,7 +39,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51879, Port = 51179,
} }
}, },
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
@ -50,7 +50,7 @@ namespace Ocelot.AcceptanceTests
var input = "people"; 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.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenThePostHasGzipContent(input)) .And(x => _steps.GivenThePostHasGzipContent(input))

View File

@ -1,446 +1,446 @@
namespace Ocelot.AcceptanceTests namespace Ocelot.AcceptanceTests
{ {
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
public class HeaderTests : IDisposable public class HeaderTests : IDisposable
{ {
private int _count; private int _count;
private readonly Steps _steps; private readonly Steps _steps;
private readonly ServiceHandler _serviceHandler; private readonly ServiceHandler _serviceHandler;
public HeaderTests() public HeaderTests()
{ {
_serviceHandler = new ServiceHandler(); _serviceHandler = new ServiceHandler();
_steps = new Steps(); _steps = new Steps();
} }
[Fact] [Fact]
public void should_transform_upstream_header() public void should_transform_upstream_header()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamHostAndPorts = new List<FileHostAndPort>
{ {
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51871, Port = 51871,
} }
}, },
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
UpstreamHeaderTransform = new Dictionary<string,string> UpstreamHeaderTransform = new Dictionary<string,string>
{ {
{"Laz", "D, GP"} {"Laz", "D, GP"}
} }
} }
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51871", "/", 200, "Laz")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51871", "/", 200, "Laz"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIAddAHeader("Laz", "D")) .And(x => _steps.GivenIAddAHeader("Laz", "D"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("GP")) .And(x => _steps.ThenTheResponseBodyShouldBe("GP"))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void should_transform_downstream_header() public void should_transform_downstream_header()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamHostAndPorts = new List<FileHostAndPort>
{ {
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51871, Port = 51871,
} }
}, },
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
DownstreamHeaderTransform = new Dictionary<string,string> DownstreamHeaderTransform = new Dictionary<string,string>
{ {
{"Location", "http://www.bbc.co.uk/, http://ocelot.com/"} {"Location", "http://www.bbc.co.uk/, http://ocelot.com/"}
} }
} }
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51871", "/", 200, "Location", "http://www.bbc.co.uk/")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51871", "/", 200, "Location", "http://www.bbc.co.uk/"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseHeaderIs("Location", "http://ocelot.com/")) .And(x => _steps.ThenTheResponseHeaderIs("Location", "http://ocelot.com/"))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void should_fix_issue_190() public void should_fix_issue_190()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamHostAndPorts = new List<FileHostAndPort>
{ {
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 6773, Port = 6773,
} }
}, },
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
DownstreamHeaderTransform = new Dictionary<string,string> DownstreamHeaderTransform = new Dictionary<string,string>
{ {
{"Location", "http://localhost:6773, {BaseUrl}"} {"Location", "http://localhost:6773, {BaseUrl}"}
}, },
HttpHandlerOptions = new FileHttpHandlerOptions HttpHandlerOptions = new FileHttpHandlerOptions
{ {
AllowAutoRedirect = false AllowAutoRedirect = false
} }
} }
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6773", "/", 302, "Location", "http://localhost:6773/pay/Receive")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6773", "/", 302, "Location", "http://localhost:6773/pay/Receive"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Redirect)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Redirect))
.And(x => _steps.ThenTheResponseHeaderIs("Location", "http://localhost:5000/pay/Receive")) .And(x => _steps.ThenTheResponseHeaderIs("Location", "http://localhost:5000/pay/Receive"))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void should_fix_issue_205() public void should_fix_issue_205()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamHostAndPorts = new List<FileHostAndPort>
{ {
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 6773, Port = 6773,
} }
}, },
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
DownstreamHeaderTransform = new Dictionary<string,string> DownstreamHeaderTransform = new Dictionary<string,string>
{ {
{"Location", "{DownstreamBaseUrl}, {BaseUrl}"} {"Location", "{DownstreamBaseUrl}, {BaseUrl}"}
}, },
HttpHandlerOptions = new FileHttpHandlerOptions HttpHandlerOptions = new FileHttpHandlerOptions
{ {
AllowAutoRedirect = false AllowAutoRedirect = false
} }
} }
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6773", "/", 302, "Location", "http://localhost:6773/pay/Receive")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6773", "/", 302, "Location", "http://localhost:6773/pay/Receive"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Redirect)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Redirect))
.And(x => _steps.ThenTheResponseHeaderIs("Location", "http://localhost:5000/pay/Receive")) .And(x => _steps.ThenTheResponseHeaderIs("Location", "http://localhost:5000/pay/Receive"))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void should_fix_issue_417() public void should_fix_issue_417()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamHostAndPorts = new List<FileHostAndPort>
{ {
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 6773, Port = 6773,
} }
}, },
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
DownstreamHeaderTransform = new Dictionary<string,string> DownstreamHeaderTransform = new Dictionary<string,string>
{ {
{"Location", "{DownstreamBaseUrl}, {BaseUrl}"} {"Location", "{DownstreamBaseUrl}, {BaseUrl}"}
}, },
HttpHandlerOptions = new FileHttpHandlerOptions HttpHandlerOptions = new FileHttpHandlerOptions
{ {
AllowAutoRedirect = false AllowAutoRedirect = false
} }
} }
}, },
GlobalConfiguration = new FileGlobalConfiguration GlobalConfiguration = new FileGlobalConfiguration
{ {
BaseUrl = "http://anotherapp.azurewebsites.net" BaseUrl = "http://anotherapp.azurewebsites.net"
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6773", "/", 302, "Location", "http://localhost:6773/pay/Receive")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6773", "/", 302, "Location", "http://localhost:6773/pay/Receive"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Redirect)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Redirect))
.And(x => _steps.ThenTheResponseHeaderIs("Location", "http://anotherapp.azurewebsites.net/pay/Receive")) .And(x => _steps.ThenTheResponseHeaderIs("Location", "http://anotherapp.azurewebsites.net/pay/Receive"))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void request_should_reuse_cookies_with_cookie_container() public void request_should_reuse_cookies_with_cookie_container()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/sso/{everything}", DownstreamPathTemplate = "/sso/{everything}",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamHostAndPorts = new List<FileHostAndPort>
{ {
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 6774, Port = 6774,
} }
}, },
UpstreamPathTemplate = "/sso/{everything}", UpstreamPathTemplate = "/sso/{everything}",
UpstreamHttpMethod = new List<string> { "Get", "Post", "Options" }, UpstreamHttpMethod = new List<string> { "Get", "Post", "Options" },
HttpHandlerOptions = new FileHttpHandlerOptions HttpHandlerOptions = new FileHttpHandlerOptions
{ {
UseCookieContainer = true UseCookieContainer = true
} }
} }
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6774", "/sso/test", 200)) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6774", "/sso/test", 200))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.WhenIGetUrlOnTheApiGateway("/sso/test")) .And(x => _steps.WhenIGetUrlOnTheApiGateway("/sso/test"))
.And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseHeaderIs("Set-Cookie", "test=0; path=/")) .And(x => _steps.ThenTheResponseHeaderIs("Set-Cookie", "test=0; path=/"))
.And(x => _steps.GivenIAddCookieToMyRequest("test=1; path=/")) .And(x => _steps.GivenIAddCookieToMyRequest("test=1; path=/"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/sso/test")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/sso/test"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void request_should_have_own_cookies_no_cookie_container() public void request_should_have_own_cookies_no_cookie_container()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/sso/{everything}", DownstreamPathTemplate = "/sso/{everything}",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamHostAndPorts = new List<FileHostAndPort>
{ {
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 6775, Port = 6775,
} }
}, },
UpstreamPathTemplate = "/sso/{everything}", UpstreamPathTemplate = "/sso/{everything}",
UpstreamHttpMethod = new List<string> { "Get", "Post", "Options" }, UpstreamHttpMethod = new List<string> { "Get", "Post", "Options" },
HttpHandlerOptions = new FileHttpHandlerOptions HttpHandlerOptions = new FileHttpHandlerOptions
{ {
UseCookieContainer = false UseCookieContainer = false
} }
} }
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6775", "/sso/test", 200)) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6775", "/sso/test", 200))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.WhenIGetUrlOnTheApiGateway("/sso/test")) .And(x => _steps.WhenIGetUrlOnTheApiGateway("/sso/test"))
.And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseHeaderIs("Set-Cookie", "test=0; path=/")) .And(x => _steps.ThenTheResponseHeaderIs("Set-Cookie", "test=0; path=/"))
.And(x => _steps.GivenIAddCookieToMyRequest("test=1; path=/")) .And(x => _steps.GivenIAddCookieToMyRequest("test=1; path=/"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/sso/test")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/sso/test"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void issue_474_should_not_put_spaces_in_header() public void issue_474_should_not_put_spaces_in_header()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamHostAndPorts = new List<FileHostAndPort>
{ {
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51879, Port = 52866,
} }
}, },
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Accept")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:52866", "/", 200, "Accept"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIAddAHeader("Accept", "text/html,application/xhtml+xml,application/xml;")) .And(x => _steps.GivenIAddAHeader("Accept", "text/html,application/xhtml+xml,application/xml;"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("text/html,application/xhtml+xml,application/xml;")) .And(x => _steps.ThenTheResponseBodyShouldBe("text/html,application/xhtml+xml,application/xml;"))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void issue_474_should_put_spaces_in_header() public void issue_474_should_put_spaces_in_header()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamHostAndPorts = new List<FileHostAndPort>
{ {
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51879, Port = 51874,
} }
}, },
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Accept")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51874", "/", 200, "Accept"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIAddAHeader("Accept", "text/html")) .And(x => _steps.GivenIAddAHeader("Accept", "text/html"))
.And(x => _steps.GivenIAddAHeader("Accept", "application/xhtml+xml")) .And(x => _steps.GivenIAddAHeader("Accept", "application/xhtml+xml"))
.And(x => _steps.GivenIAddAHeader("Accept", "application/xml")) .And(x => _steps.GivenIAddAHeader("Accept", "application/xml"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("text/html, application/xhtml+xml, application/xml")) .And(x => _steps.ThenTheResponseBodyShouldBe("text/html, application/xhtml+xml, application/xml"))
.BDDfy(); .BDDfy();
} }
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode) private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode)
{ {
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, context => _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, context =>
{ {
if (_count == 0) if (_count == 0)
{ {
context.Response.Cookies.Append("test", "0"); context.Response.Cookies.Append("test", "0");
_count++; _count++;
context.Response.StatusCode = statusCode; context.Response.StatusCode = statusCode;
return Task.CompletedTask; return Task.CompletedTask;
} }
if (context.Request.Cookies.TryGetValue("test", out var cookieValue) || context.Request.Headers.TryGetValue("Set-Cookie", out var headerValue)) 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=/") if (cookieValue == "0" || headerValue == "test=1; path=/")
{ {
context.Response.StatusCode = statusCode; context.Response.StatusCode = statusCode;
return Task.CompletedTask; return Task.CompletedTask;
} }
} }
context.Response.StatusCode = 500; context.Response.StatusCode = 500;
return Task.CompletedTask; return Task.CompletedTask;
}); });
} }
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string headerKey) private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string headerKey)
{ {
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context => _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
{ {
if (context.Request.Headers.TryGetValue(headerKey, out var values)) if (context.Request.Headers.TryGetValue(headerKey, out var values))
{ {
var result = values.First(); var result = values.First();
context.Response.StatusCode = statusCode; context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(result); await context.Response.WriteAsync(result);
} }
}); });
} }
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string headerKey, string headerValue) private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string headerKey, string headerValue)
{ {
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, context => _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, context =>
{ {
context.Response.OnStarting(() => context.Response.OnStarting(() =>
{ {
context.Response.Headers.Add(headerKey, headerValue); context.Response.Headers.Add(headerKey, headerValue);
context.Response.StatusCode = statusCode; context.Response.StatusCode = statusCode;
return Task.CompletedTask; return Task.CompletedTask;
}); });
return Task.CompletedTask; return Task.CompletedTask;
}); });
} }
public void Dispose() public void Dispose()
{ {
_serviceHandler?.Dispose(); _serviceHandler?.Dispose();
_steps.Dispose(); _steps.Dispose();
} }
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -52,7 +52,8 @@
[Fact] [Fact]
public void should_return_internal_server_error_if_downstream_service_returns_internal_server_error() public void should_return_internal_server_error_if_downstream_service_returns_internal_server_error()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
@ -81,6 +82,39 @@
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.InternalServerError)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.InternalServerError))
.BDDfy(); .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) private void GivenThereIsAServiceRunningOn(string url)

View File

@ -84,14 +84,14 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", 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.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
@ -131,7 +131,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51879, Port = 50810,
} }
}, },
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
@ -180,7 +180,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51879, Port = 50810,
} }
}, },
UpstreamPathTemplate = "/", 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.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
@ -263,7 +263,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51879, Port = 51005,
} }
}, },
UpstreamPathTemplate = "/{url}", 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.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("")) .When(x => _steps.WhenIGetUrlOnTheApiGateway(""))
@ -297,7 +297,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51879, Port = 58589,
} }
}, },
UpstreamPathTemplate = "/", 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.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
@ -382,7 +382,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51879, Port = 51206,
} }
}, },
UpstreamPathTemplate = "/", 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.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
@ -416,7 +416,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51879, Port = 51990,
} }
}, },
UpstreamPathTemplate = "/", 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.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
@ -450,7 +450,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51879, Port = 58804,
} }
}, },
UpstreamPathTemplate = "/products/", 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.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/products"))
@ -484,7 +484,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51879, Port = 54015,
} }
}, },
UpstreamPathTemplate = "/products", 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.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/"))
@ -517,7 +517,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51879, Port = 54072,
} }
}, },
UpstreamPathTemplate = "/products/{productId}", 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.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/"))
@ -550,7 +550,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51879, Port = 55961,
} }
}, },
UpstreamPathTemplate = "/products/{productId}", 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.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/1")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/1"))
@ -584,7 +584,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51879, Port = 51116,
} }
}, },
UpstreamPathTemplate = "/{variantId}/products/{productId}", 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.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("23/products/1")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("23/products/1"))
@ -618,7 +618,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51879, Port = 51809,
} }
}, },
UpstreamPathTemplate = "/products/{productId}", 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.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/1")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/1"))
@ -650,7 +650,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51879, Port = 56615,
} }
}, },
DownstreamScheme = "http", 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.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenThePostHasContent("postContent")) .And(x => _steps.GivenThePostHasContent("postContent"))
@ -686,7 +686,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51879, Port = 57771,
} }
}, },
UpstreamHttpMethod = new List<string> { "Get" }, 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.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .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=-")) .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 new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51879, Port = 55609,
} }
}, },
UpstreamPathTemplate = "/myApp1Name/api/{urlPath}", 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.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/myApp1Name/api/products/1")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/myApp1Name/api/products/1"))
@ -752,7 +752,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51879, Port = 59911,
} }
}, },
DownstreamScheme = "http", 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.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenThePostHasContent("postContent")) .And(x => _steps.GivenThePostHasContent("postContent"))
@ -786,7 +786,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51879, Port = 59187,
} }
}, },
DownstreamScheme = "http", 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.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
@ -821,7 +821,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51879, Port = 54079,
} }
}, },
UpstreamPathTemplate = "/vacancy/", UpstreamPathTemplate = "/vacancy/",
@ -837,7 +837,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51879, Port = 54079,
} }
}, },
UpstreamPathTemplate = "/vacancy/{vacancyId}", 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.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("api/vacancy/1")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("api/vacancy/1"))

View File

@ -10,12 +10,14 @@
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Moq;
using Newtonsoft.Json; using Newtonsoft.Json;
using Ocelot.Cache.CacheManager; using Ocelot.Cache.CacheManager;
using Ocelot.Configuration.Creator; using Ocelot.Configuration.Creator;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Ocelot.DependencyInjection; using Ocelot.DependencyInjection;
using Ocelot.Infrastructure; using Ocelot.Infrastructure;
using Ocelot.Logging;
using Ocelot.Middleware; using Ocelot.Middleware;
using Ocelot.Middleware.Multiplexer; using Ocelot.Middleware.Multiplexer;
using Ocelot.Provider.Consul; using Ocelot.Provider.Consul;
@ -1120,5 +1122,60 @@
_ocelotClient = _ocelotServer.CreateClient(); _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> <PropertyGroup>
<VersionPrefix>0.0.0-dev</VersionPrefix> <VersionPrefix>0.0.0-dev</VersionPrefix>
<TargetFramework>netcoreapp3.0</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<AssemblyName>Ocelot.Benchmarks</AssemblyName> <AssemblyName>Ocelot.Benchmarks</AssemblyName>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<PackageId>Ocelot.Benchmarks</PackageId> <PackageId>Ocelot.Benchmarks</PackageId>
@ -25,6 +25,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-19367-01" /> <PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

@ -41,14 +41,14 @@ namespace Ocelot.UnitTests.Configuration
_internalConfigCreator = new Mock<IInternalConfigurationCreator>(); _internalConfigCreator = new Mock<IInternalConfigurationCreator>();
_internalConfigCreator.Setup(x => x.Create(It.IsAny<FileConfiguration>())).ReturnsAsync(new OkResponse<IInternalConfiguration>(_internalConfig)); _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 = new FileConfigurationPoller(_factory.Object, _repo.Object, _config.Object, _internalConfigRepo.Object, _internalConfigCreator.Object);
_poller.StartAsync(new CancellationToken());
} }
[Fact] [Fact]
public void should_start() public void should_start()
{ {
this.Given(x => ThenTheSetterIsCalled(_fileConfig, 1)) this.Given(x => GivenPollerHasStarted())
.BDDfy(); .Given(x => ThenTheSetterIsCalled(_fileConfig, 1))
.BDDfy();
} }
[Fact] [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)) .Then(x => ThenTheSetterIsCalledAtLeast(newConfig, 1))
.BDDfy(); .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)) .Then(x => ThenTheSetterIsCalled(newConfig, 1))
.BDDfy(); .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)) .Then(x => ThenTheSetterIsCalled(newConfig, 0))
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_dispose_cleanly_without_starting()
{
this.When(x => WhenPollerIsDisposed())
.BDDfy();
}
private void GivenPollerHasStarted()
{
_poller.StartAsync(CancellationToken.None);
}
private void WhenProviderErrors() private void WhenProviderErrors()
{ {
_repo _repo
@ -141,6 +156,11 @@ namespace Ocelot.UnitTests.Configuration
.ReturnsAsync(new OkResponse<FileConfiguration>(newConfig)); .ReturnsAsync(new OkResponse<FileConfiguration>(newConfig));
} }
private void WhenPollerIsDisposed()
{
_poller.Dispose();
}
private void ThenTheSetterIsCalled(FileConfiguration fileConfig, int times) private void ThenTheSetterIsCalled(FileConfiguration fileConfig, int times)
{ {
var result = WaitFor(4000).Until(() => 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)) this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions()) .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)) this.Given(x => GivenTheFollowing(fileReRoute))
.And(x => GivenARealTracer()) .And(x => GivenARealTracer())
@ -73,7 +73,7 @@ namespace Ocelot.UnitTests.Configuration
public void should_create_options_with_useCookie_false_and_allowAutoRedirect_true_as_default() public void should_create_options_with_useCookie_false_and_allowAutoRedirect_true_as_default()
{ {
var fileReRoute = new FileReRoute(); 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)) this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions()) .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)) this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions()) .When(x => WhenICreateHttpHandlerOptions())
@ -110,7 +110,7 @@ namespace Ocelot.UnitTests.Configuration
HttpHandlerOptions = new FileHttpHandlerOptions() 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)) this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions()) .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)) this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions()) .When(x => WhenICreateHttpHandlerOptions())
@ -154,6 +211,7 @@ namespace Ocelot.UnitTests.Configuration
_httpHandlerOptions.UseCookieContainer.ShouldBe(expected.UseCookieContainer); _httpHandlerOptions.UseCookieContainer.ShouldBe(expected.UseCookieContainer);
_httpHandlerOptions.UseTracing.ShouldBe(expected.UseTracing); _httpHandlerOptions.UseTracing.ShouldBe(expected.UseTracing);
_httpHandlerOptions.UseProxy.ShouldBe(expected.UseProxy); _httpHandlerOptions.UseProxy.ShouldBe(expected.UseProxy);
_httpHandlerOptions.MaxConnectionsPerServer.ShouldBe(expected.MaxConnectionsPerServer);
} }
private void GivenARealTracer() private void GivenARealTracer()

View File

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

View File

@ -1341,6 +1341,32 @@
.BDDfy(); .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) private void GivenAConfiguration(FileConfiguration fileConfiguration)
{ {
_fileConfiguration = fileConfiguration; _fileConfiguration = fileConfiguration;

View File

@ -350,6 +350,36 @@
.BDDfy(); .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) private void GivenTheServiceProviderConfigIs(ServiceProviderConfiguration config)
{ {
var configuration = new InternalConfiguration(null, null, config, null, null, null, null, null); 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; using Ocelot.Middleware;
namespace Ocelot.UnitTests.LoadBalancer namespace Ocelot.UnitTests.LoadBalancer
@ -108,6 +110,26 @@ namespace Ocelot.UnitTests.LoadBalancer
.BDDfy(); .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() private void WhenICallTheMiddleware()
{ {
_middleware = new LoadBalancingMiddleware(_next, _loggerFactory.Object, _loadBalancerHouse.Object); _middleware = new LoadBalancingMiddleware(_next, _loggerFactory.Object, _loadBalancerHouse.Object);
@ -135,6 +157,13 @@ namespace Ocelot.UnitTests.LoadBalancer
.ReturnsAsync(_getHostAndPortError); .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() private void GivenTheLoadBalancerReturns()
{ {
_hostAndPort = new ServiceHostAndPort("127.0.0.1", 80); _hostAndPort = new ServiceHostAndPort("127.0.0.1", 80);
@ -186,6 +215,13 @@ namespace Ocelot.UnitTests.LoadBalancer
_downstreamContext.Errors.ShouldBe(_getHostAndPortError.Errors); _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) private void ThenTheDownstreamUrlIsReplacedWith(string expectedUri)
{ {
_downstreamContext.DownstreamRequest.ToHttpRequestMessage().RequestUri.OriginalString.ShouldBe(expectedUri); _downstreamContext.DownstreamRequest.ToHttpRequestMessage().RequestUri.OriginalString.ShouldBe(expectedUri);

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