├── .EditorConfig
├── .gitignore
├── .sonarqube
├── conf
│ ├── SonarQubeAnalysisConfig.xml
│ ├── SonarQubeRoslyn-cs.ruleset
│ ├── SonarQubeRoslyn-vbnet.ruleset
│ ├── cs
│ │ └── SonarLint.xml
│ └── vbnet
│ │ └── SonarLint.xml
└── out
│ └── summary.md
├── .travis.yml
├── CondenserDotNet.sln
├── Dump
├── Class1.cs
└── Dump.csproj
├── LICENSE.md
├── NuGet.Config
├── README.md
├── VersionNumber.bat
├── appveyor.yml
├── common.props
├── coverage.xml
├── depend.props
├── releasenotes
├── 2.1.1.props
├── 2.1.2.props
├── 2.1.3.props
├── 2.2.0.props
├── 2.3.0.props
├── 2.4.0.props
├── 2.4.1.props
├── 2.5.1.props
├── 2.5.2.props
├── 2.5.3.props
├── 2.5.4.props
├── 2.6.0.props
├── 2.6.1.props
├── 2.6.2.props
├── 2.6.3.props
├── 2.6.4.props
├── 2.6.5.props
├── 2.6.6.props
├── 2.6.7.props
├── 2.6.8.props
├── 2.7.0.props
├── 2.7.1.props
├── 2.8.0.props
├── 2.8.1.props
├── 2.8.2.props
├── 3.0.0.props
├── 3.0.1.props
├── 3.0.10.props
├── 3.0.11.props
├── 3.0.12.props
├── 3.0.2.props
├── 3.0.3.props
├── 3.0.4.props
├── 3.0.5.props
├── 3.0.6.props
├── 3.0.7.props
├── 3.0.8.props
├── 3.0.9.props
├── 3.1.0.props
├── 3.2.0.props
├── 3.2.2.props
├── 4.0.0.props
├── 4.0.1.props
├── 4.0.2.props
├── 4.0.3.props
├── 4.1.0.props
├── 4.1.1.props
├── 4.1.10.props
├── 4.1.11.props
├── 4.1.12.props
├── 4.1.13.props
├── 4.1.2.props
├── 4.1.3.props
├── 4.1.4.props
├── 4.1.5.props
├── 4.1.6.props
├── 4.1.7.props
├── 4.1.8.props
├── 4.1.9.props
├── 5.0.0.props
├── 5.1.0.props
├── 5.2.0.props
└── 5.2.1.props
├── samples
├── Configuration
│ ├── Configuration.csproj
│ ├── ConsulConfig.cs
│ ├── Controllers
│ │ └── ConfigController.cs
│ ├── Program.cs
│ ├── Properties
│ │ ├── AssemblyInfo.cs
│ │ └── launchSettings.json
│ └── Startup.cs
├── PocWebsocketsSupport
│ ├── PocWebsocketsSupport.csproj
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ └── Startup.cs
├── ProtocolSwitchingTest
│ ├── GlobalSuppressions.cs
│ ├── Program.cs
│ ├── ProtocolSwitchingTest.csproj
│ ├── Startup.cs
│ └── TestController.cs
├── Routing
│ ├── MyMiddleware.cs
│ ├── Program.cs
│ ├── Properties
│ │ ├── AssemblyInfo.cs
│ │ └── launchSettings.json
│ ├── Routing.csproj
│ └── Startup.cs
├── RoutingWithWindowsAuth
│ ├── Program.cs
│ ├── Properties
│ │ ├── AssemblyInfo.cs
│ │ └── launchSettings.json
│ ├── RoutingWithWindowsAuth.csproj
│ └── Startup.cs
├── ServiceRegistration
│ ├── Controllers
│ │ ├── HealthController.cs
│ │ └── SomeController.cs
│ ├── Program.cs
│ ├── Properties
│ │ ├── AssemblyInfo.cs
│ │ └── launchSettings.json
│ ├── ServiceRegistration.csproj
│ └── Startup.cs
├── WebsocketClient
│ ├── GlobalSuppressions.cs
│ ├── Program.cs
│ └── WebsocketClient.csproj
└── WebsocketSampleServer
│ ├── Controllers
│ └── HealthController.cs
│ ├── Program.cs
│ ├── Startup.cs
│ └── WebsocketSampleServer.csproj
├── script
└── runtests.bat
├── src
├── CondenserDotNet.Client
│ ├── CondenserDotNet.Client.csproj
│ ├── DataContracts
│ │ ├── HealthCheck.cs
│ │ ├── Service.cs
│ │ ├── SessionCreate.cs
│ │ └── SessionCreateResponse.cs
│ ├── GlobalSuppressions.cs
│ ├── HealthConfiguration.cs
│ ├── IServiceManager.cs
│ ├── ITtlCheck.cs
│ ├── Leadership
│ │ ├── ILeaderRegistry.cs
│ │ ├── ILeaderWatcher.cs
│ │ ├── LeaderRegistry.cs
│ │ └── LeaderWatcherNew.cs
│ ├── MaintenanceExtensions.cs
│ ├── RegistrationExtensions.cs
│ ├── ServiceCollectionExtensions.cs
│ ├── ServiceManager.cs
│ ├── ServiceManagerConfig.cs
│ ├── Services
│ │ ├── IServiceRegistry.cs
│ │ ├── NoConsulConnectionException.cs
│ │ ├── NoServiceInstanceFoundException.cs
│ │ ├── ServiceBasedHttpHandler.cs
│ │ ├── ServiceRegistry.cs
│ │ ├── ServiceRegistryDelegatingHandler.cs
│ │ ├── ServiceRegistryNearestDelegatingHandler.cs
│ │ ├── ServiceWatcher.cs
│ │ └── WatcherState.cs
│ └── TtlCheck.cs
├── CondenserDotNet.Configuration
│ ├── CondenserConfigBuilder.cs
│ ├── CondenserDotNet.Configuration.csproj
│ ├── ConfigurationBuilderExtensions.cs
│ ├── ConfigurationRegistryExtensions.cs
│ ├── ConfigurationRegistryProvider.cs
│ ├── ConfigurationRegistrySource.cs
│ ├── Consul
│ │ ├── ConfigurationWatcher.cs
│ │ ├── ConsulConfigSource.cs
│ │ ├── ConsulRegistry.cs
│ │ ├── ConsulRegistryConfig.cs
│ │ ├── IConfigSource.cs
│ │ ├── IKeyParser.cs
│ │ ├── JsonKeyValueParser.cs
│ │ ├── KeyOperationResult.cs
│ │ ├── KeyValue.cs
│ │ └── SimpleKeyValueParser.cs
│ ├── IConfigurationRegistry.cs
│ └── ServiceCollectionExtensions.cs
├── CondenserDotNet.Core
│ ├── AsyncManualResetEvent.cs
│ ├── CondenserDotNet.Core.csproj
│ ├── CondenserEventSource.cs
│ ├── Consul
│ │ ├── EncryptedConsulKeyAclProvider.cs
│ │ ├── IConsulAclProvider.cs
│ │ ├── KeyValue.cs
│ │ └── StaticConsulAclProvider.cs
│ ├── DataContracts
│ │ ├── InformationCheck.cs
│ │ ├── InformationNode.cs
│ │ ├── InformationService.cs
│ │ └── InformationServiceSet.cs
│ ├── HttpUtils.cs
│ ├── RandHelper.cs
│ ├── Routing
│ │ ├── DefaultRouting.cs
│ │ ├── IDefaultRouting.cs
│ │ ├── IRoutingConfig.cs
│ │ ├── IRoutingStrategy.cs
│ │ ├── RandomRouteNode.cs
│ │ ├── RoundRobinRoutingStrategy.cs
│ │ ├── RouteStrategy.cs
│ │ └── UseTopRouting.cs
│ └── ServiceUtils.cs
├── CondenserDotNet.Middleware
│ ├── CleanShutdown
│ │ ├── CleanShutdownExtensions.cs
│ │ ├── CleanShutdownMiddleware.cs
│ │ └── CleanShutdownService.cs
│ ├── CondenserDotNet.Middleware.csproj
│ ├── ConsulCleanShutdown
│ │ ├── ConsulShutdownExtensions.cs
│ │ └── ConsulShutdownService.cs
│ ├── TrailingHeaders
│ │ ├── ChunkingStream.cs
│ │ ├── ITrailingHeadersFeature.cs
│ │ ├── TrailingHeadersFeature.cs
│ │ └── TrailingHeadersMiddleware.cs
│ └── WindowsAuthentication
│ │ ├── AuthenticationConnectionMiddleware.cs
│ │ ├── IWindowsAuthFeature.cs
│ │ ├── Interop
│ │ └── Windows
│ │ │ ├── Interop.Libraries.cs
│ │ │ ├── Kernel32
│ │ │ └── Interop.CloseHandle .cs
│ │ │ └── Secur32
│ │ │ ├── Interop.AcceptSecurityContext.cs
│ │ │ ├── Interop.AcquireCredentialsHandle.cs
│ │ │ ├── Interop.DeleteSecurityContext.cs
│ │ │ ├── Interop.Flags.cs
│ │ │ ├── Interop.FreeCredentialsHandle.cs
│ │ │ ├── Interop.QuerySecurityContextToken .cs
│ │ │ ├── Interop.SecurityBuffers.cs
│ │ │ ├── Interop.SecurityHandle.cs
│ │ │ └── Interop.SecurityInteger.cs
│ │ ├── WindowsAuthFeature.cs
│ │ ├── WindowsAuthenticationExtensions.cs
│ │ ├── WindowsAuthenticationMiddleware.cs
│ │ └── WindowsHandshake.cs
└── CondenserDotNet.Stats
│ ├── CondenserDotNet.Stats.csproj
│ ├── MetricEntry.cs
│ ├── MetricType.cs
│ ├── SocketUdpConnecction.cs
│ ├── StatsDClient.cs
│ └── UpdStatsD.cs
├── test
├── Condenser.FullFramework
│ ├── Condenser.FullFramework.csproj
│ ├── SwitcherRooFacts.cs
│ └── xunit.runner.json
├── Condenser.Tests.Integration
│ ├── AuthenticationMiddlewareFacts.cs
│ ├── Condenser.Tests.Integration.csproj
│ ├── ConfigFacts.cs
│ ├── Internal
│ │ └── WindowsOnlyFact.cs
│ ├── LeadershipFacts.cs
│ ├── ListCallbackFacts.cs
│ ├── MaintenanceFacts.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── ProtocolSwitcherFacts.cs
│ ├── ServiceDeregistrationFacts.cs
│ ├── ServiceLookupFacts.cs
│ ├── ServiceRegistrationFacts.cs
│ ├── TestCert.pfx
│ ├── TrailingHeaderMiddlewareFacts.cs
│ └── xunit.runner.json
└── CondenserTests
│ ├── CondenserTests.csproj
│ ├── ConsulConfigurationProviderTests.cs
│ ├── Fakes
│ ├── FakeConfig.cs
│ ├── FakeConfigurationRegistry.cs
│ ├── FakeController.cs
│ └── FakeServiceManager.cs
│ ├── GlobalSuppressions.cs
│ ├── HealthConfigurationFacts.cs
│ ├── JsonKeyValueParserTests.cs
│ ├── Properties
│ └── AssemblyInfo.cs
│ ├── RountingStrategyFacts.cs
│ ├── ServiceManagerConfigurationFacts.cs
│ ├── TagsToRoutesFacts.cs
│ ├── TestServerFacts.cs
│ └── xunit.runner.json
└── version.props
/.sonarqube/conf/SonarQubeRoslyn-vbnet.ruleset:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/.sonarqube/out/summary.md:
--------------------------------------------------------------------------------
1 | Analysis failed for SonarQube project "", version
2 | - Product projects: 0, test projects: 0
3 | - Invalid projects: 0, skipped projects: 0, excluded projects: 0
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: generic
2 |
3 | addons:
4 | apt:
5 | packages:
6 | - gettext
7 | - libcurl4-openssl-dev
8 | - libicu-dev
9 | - libssl-dev
10 | - libunwind8
11 | - zlib1g
12 |
13 | matrix:
14 | include:
15 | - os: linux
16 | dist: trusty
17 | sudo: required
18 |
19 | before_install:
20 | # Install OpenSSL
21 | - if test "$TRAVIS_OS_NAME" == "osx"; then
22 | brew install openssl;
23 | brew link --force openssl;
24 | export DOTNET_SDK_URL="https://go.microsoft.com/fwlink/?linkid=843444";
25 | else
26 | export DOTNET_SDK_URL="https://go.microsoft.com/fwlink/?linkid=843450";
27 | fi
28 |
29 | - export DOTNET_INSTALL_DIR="$PWD/.dotnetcli"
30 |
31 | # Install .NET CLI
32 | - mkdir $DOTNET_INSTALL_DIR
33 | - curl -L $DOTNET_SDK_URL -o dotnet_package
34 | - tar -xvzf dotnet_package -C $DOTNET_INSTALL_DIR
35 |
36 | # Add dotnet to PATH
37 | - export PATH="$DOTNET_INSTALL_DIR:$PATH"
38 |
39 | install:
40 | # Display dotnet version info
41 | - which dotnet;
42 | if [ $? -eq 0 ]; then
43 | echo "Using dotnet:";
44 | dotnet --info;
45 | else
46 | echo "dotnet.exe not found"
47 | exit 1;
48 | fi
49 | # Restore dependencies
50 | - dotnet restore
51 |
52 | before_script:
53 | # Install consul
54 | - if test "$TRAVIS_OS_NAME" == "osx"; then
55 | wget 'https://releases.hashicorp.com/consul/0.7.0/consul_0.7.0_darwin_amd64.zip';
56 | unzip "consul_0.7.0_darwin_amd64.zip";
57 | else
58 | wget 'https://releases.hashicorp.com/consul/0.7.0/consul_0.7.0_linux_amd64.zip';
59 | unzip "consul_0.7.0_linux_amd64.zip";
60 | fi
61 | - ./consul --version
62 |
63 | script:
64 | # Start Consul
65 | - ./consul agent -server -bootstrap-expect 1 -log-level err -data-dir /tmp/consul -advertise=127.0.0.1 &
66 | # Build sample
67 | - dotnet test test/CondenserTests/CondenserTests.csproj
68 | - dotnet test test/Condenser.Tests.Integration/Condenser.Tests.Integration.csproj
69 |
--------------------------------------------------------------------------------
/Dump/Class1.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Dump
4 | {
5 | public class Class1
6 | {
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/Dump/Dump.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Tim Seaward
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/NuGet.Config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | [](https://ci.appveyor.com/project/Drawaes/condenserdotnet)
4 | [](https://travis-ci.org/Drawaes/CondenserDotNet)
5 | [](https://gitter.im/CondenserDotNet/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
6 | # CondenserDotNet
7 |
8 | API Condenser / Reverse Proxy using Kestrel and Consul, Including light weight consul lib
9 |
10 | A set of consul clients for .net that is simple and integrates with an API proxy
11 |
12 | CI Builds available as nuget packs from myget
13 |
14 | [](https://www.myget.org/F/condenserdotnet/api/v3/index.json)
15 |
16 | Current release is available at
17 |
18 | [](https://www.nuget.org/packages/CondenserDotNet.Client/)
19 |
20 | [Documentation](https://drawaes.github.io/CondenserDocs/)
21 |
--------------------------------------------------------------------------------
/VersionNumber.bat:
--------------------------------------------------------------------------------
1 | IF "%APPVEYOR_REPO_TAG_NAME%"=="" (
2 | ECHO ^^^%APPVEYOR_BUILD_NUMBER%^^^ > version.props
3 | ) ELSE (
4 | ECHO ^^^^%APPVEYOR_REPO_TAG_NAME%^^^ > version.props
5 | )
6 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | os: Visual Studio 2019 Preview
2 | build: off
3 |
4 | environment:
5 | sonarkey:
6 | secure: dqF6V11A7InHKcyOX6WDGE3oA54yZQm0r9VLio85ndCn2B8d9zVI2mJ3lQdDzO3o
7 | COVERALLS_REPO_TOKEN:
8 | secure: x41DSerLXKgGVbKIokF+zuR3eNRVJXsgJA6j5yggnCB8/TTyYfa/2euNflfGzCot
9 |
10 | install:
11 | - cmd: curl -fsS -o consul.zip https://releases.hashicorp.com/consul/1.4.4/consul_1.4.4_windows_amd64.zip
12 | - cmd: 7z x consul.zip -o"C:\Consul" -y > nul
13 | - ps: $MyProcess = Start-Process C:\Consul\consul.exe -ArgumentList "agent -server -log-level err -bootstrap-expect 1 -data-dir C:\Consul\Data -advertise=127.0.0.1" -PassThru
14 |
15 | before_test:
16 | - ECHO %APPVEYOR_REPO_COMMIT_MESSAGE%
17 | - dotnet --info
18 | - VersionNumber.bat
19 | - dotnet restore
20 |
21 | test_script:
22 | # Build sample
23 | - dotnet test test/CondenserTests/CondenserTests.csproj
24 | - dotnet test test/Condenser.Tests.Integration/Condenser.Tests.Integration.csproj
25 |
26 | after_test:
27 | # Build and pack source
28 | - ps: iex ((Get-ChildItem ($env:USERPROFILE + '\.nuget\packages\OpenCover'))[0].FullName + '\tools\OpenCover.Console.exe' + ' -register:user -target:".\script\runtests.bat" -searchdirs:"..\test\Condenser.Tests.Integration\bin\Debug\netcoreapp3.10;..\test\CondenserTests\bin\debug\netcoreapp3.0" -oldstyle -output:coverage.xml -skipautoprops -hideskipped:All -returntargetcode -filter:"+[Condenser*]* -[*Test*]*"')
29 | - ps: iex ((Get-ChildItem ($env:USERPROFILE + '\.nuget\packages\coveralls.io'))[0].FullName + '\tools\coveralls.net.exe' + ' --opencover coverage.xml')
30 | - "SET PATH=C:\\Python34;C:\\Python34\\Scripts;%PATH%"
31 | - pip install codecov
32 | - codecov -f "coverage.xml"
33 | - dotnet build -c Release
34 | - dotnet pack -c Release src/CondenserDotNet.Client
35 | - dotnet pack -c Release src/CondenserDotNet.Core
36 | - dotnet pack -c Release src/CondenserDotNet.Configuration
37 | - dotnet pack -c Release src/CondenserDotNet.Middleware
38 |
39 |
40 | artifacts:
41 | - path: '**/*.nupkg'
42 | name: packages
43 | - path: 'coverage.xml'
44 | name: coverage
45 | type: zip
46 |
47 | deploy:
48 | - provider: NuGet
49 | server: https://www.myget.org/F/condenserdotnet/api/v2/package
50 | api_key:
51 | secure: F4U6pELKG7NqzEZXyr7WmOySddam2tpYB1LqAPlJAKGT2WRukOZRtWk8ZBuZ+nzs
52 | skip_symbols: true
53 | on:
54 | branch: master
55 |
--------------------------------------------------------------------------------
/common.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Condenser API Router
5 | $(VersionSuffix)-$(BuildNumber)
6 | https://github.com/Drawaes/CondenserDotNet/blob/master/LICENSE.md
7 | true
8 | git
9 | https://github.com/Drawaes/CondenserDotNet
10 | true
11 | true
12 | $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
13 | Drawaes Consulting Limited
14 | Tim Seaward
15 | https://drawaes.github.io/CondenserDocs/
16 | full
17 | https://drawaes.github.io/CondenserDocs/assets/img/logo.png
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/coverage.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/depend.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1.6.1
4 |
5 |
--------------------------------------------------------------------------------
/releasenotes/2.1.1.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Added more unit tests
5 | * Fixed bug - response code stats not correctly recording
6 | * Fixed bug - incorrect registration of http client factory when using builder
7 |
8 |
9 |
--------------------------------------------------------------------------------
/releasenotes/2.1.2.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Added more unit tests
5 | * Fixed bug - response code stats not correctly recording
6 | * Fixed bug - incorrect registration of http client factory when using builder
7 |
8 |
9 |
--------------------------------------------------------------------------------
/releasenotes/2.1.3.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Fixed ServiceId bug where the override is ignored
5 | * Added Https support for the health checks and moved the building of the health check to the registration phase
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/2.2.0.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Added API to directly get the available service instance list via a callback
5 | * Removed TtlCheck1 property which was a duplicate from refactoring
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/2.3.0.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Changed Health API to be a simple health end point only return OK.
5 | * Added health stats API to get more detailed health checks for router
6 | * Added health stats API per service
7 | * Start building routes when routing service is started not on first request
8 |
9 |
10 |
--------------------------------------------------------------------------------
/releasenotes/2.4.0.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Added a callback on the leadership to give current leader
5 | * Added maint mode enable/disable on the service manager
6 | * Added middleware
7 | * Puts it in maint mode
8 | * Performs the cleanshutdown
9 | * Takes it out of maint mode after startup
10 |
11 |
12 |
--------------------------------------------------------------------------------
/releasenotes/2.4.1.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Reduced the number of times we update due to timeouts
5 |
6 |
7 |
--------------------------------------------------------------------------------
/releasenotes/2.5.1.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Configuration changes to create a cleaner api
5 | * Fix bug where router needs strategies to build
6 | * Dont rebuild config if consul index unchanged
7 | * Dont rebuild routes if consul index unchanged
8 |
9 |
10 |
--------------------------------------------------------------------------------
/releasenotes/2.5.2.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Fix Configuration bug that removes front character off root object
5 | * Clean up configuration API
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/releasenotes/2.5.3.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Started adding logging support to the configuration library
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/2.5.4.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Configuration fixed
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/2.6.0.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * ASP.NET version updates
5 | * Removed some dependencies
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/releasenotes/2.6.1.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Changed to 127.0.0.1
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/2.6.2.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Changed to 127.0.0.1
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/2.6.3.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Fixed service watcher bug
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/2.6.4.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Fixed service watcher bug
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/2.6.5.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Fixed service watcher bug
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/2.6.6.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Increased max server connections
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/2.6.7.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Fixed bug, if a key doesn't exist a tight loop formed without a consul index to cause blocking
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/2.6.8.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Fixed another tight loop in config watching
5 | * Fixed tight loop in Leadership
6 | * Cleaned up leadership tests
7 | * Added a leadership test to catch spinning
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/releasenotes/2.7.0.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Changed Service Registration to use hostnames instead of IP addresses
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/2.7.1.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Remove duplicates during configuration dictionary building
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/2.8.0.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Added deregister service to the extension methods
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/2.8.1.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Changed the dictionary build from a key change to not throw on duplicate key
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/2.8.2.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Changed the dictionary build from a key change to not throw on duplicate key
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/3.0.0.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Moved to ASP.NET 2.0
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/3.0.1.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Fixed the TTL to use Put rather than Get to match the API changes in Consul
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/3.0.10.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Added message to the shutdown message
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/3.0.11.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Added max server connections to the get httpclient handler
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/3.0.12.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Added http delegating handler
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/3.0.2.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Fixed the TTL to use Put rather than Get to match the API changes in Consul
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/3.0.3.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Fixed the TTL to use Put rather than Get to match the API changes in Consul
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/3.0.4.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Added information logging when started watching for a service
5 | * Added information logging when a service registers
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/releasenotes/3.0.5.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Fix the logging for service registration
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/3.0.6.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Added the ability to specify arbitary custom tags
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/3.0.7.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Added the ability to specify arbitary custom tags
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/3.0.8.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Added warning log if no instance was found by the service registry
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/3.0.9.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Added warning log if no instance was found by the service registry
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/3.1.0.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Added ability to get nearest service instance
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/3.2.0.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Added source linking
5 | * Added check for registration before attempting leadership (get watcher becomes async)
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/releasenotes/3.2.2.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Added the ability to get the service callback to the IServiceRegistry interface
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/4.0.0.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Now with added ACL support
5 | * Support for a fixed ACL Token
6 | * Support to get a encrypted base64 key from consul and use a certificate to decrypt the ACL token for use
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/releasenotes/4.0.1.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Bug fix for ACL from key path
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/4.0.2.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Added padding option for certificate based ACL decryption
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/4.0.3.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Added padding option for certificate based ACL decryption
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/4.1.0.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Added Acl Provider to the configuration registry
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/4.1.1.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Fixed Security Handle Leak in LASS
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/4.1.10.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Added ability to skip service lookup for Delegating Handlers
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/4.1.11.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Fix multi threading issue with "get all keys"
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/4.1.12.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Fix multi threading issue with "get all keys"
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/4.1.13.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Fix potential leaks by ensuring HttpResponses are disposed correctly
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/4.1.2.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Increased timeout on service watcher
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/4.1.3.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Added Eventsources for getting statistics from configuration, registration and leadership
5 | * Added updating the consul index on the first call to leadership in the outer loop
6 | * Increased the loops that occur before a retry is requested in leadership (from 2 to 10)
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/releasenotes/4.1.4.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Fix duplicate event sources
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/4.1.5.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Fix duplicate event sources
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/4.1.6.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Fix spinning on leadership election
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/4.1.7.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Fix spinning on leadership election
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/4.1.8.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Fix spinning on leadership election
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/4.1.9.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Embedding Sources
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/5.0.0.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Upgraded middleware package to Aspnet Core 3.0
5 | * Moved project to .net standard 2.1 and upgraded librarys
6 | * Removed unneeded package references
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/releasenotes/5.1.0.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Added ability to request a single key
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/5.2.0.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Added ConfigureAwait(false) to support legacy asp.net
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/releasenotes/5.2.1.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | * Added refresh session when leadership has 3 failures
5 | * Reset Consul Index on service lookup when there is a failure
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/samples/Configuration/Configuration.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.0
5 | Configuration
6 | exe
7 | Configuration
8 | false
9 | false
10 | false
11 |
12 | exe
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/samples/Configuration/ConsulConfig.cs:
--------------------------------------------------------------------------------
1 | namespace Configuration
2 | {
3 | public class ConsulConfig
4 | {
5 | public string Setting { get; set; }
6 | }
7 | }
--------------------------------------------------------------------------------
/samples/Configuration/Controllers/ConfigController.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc;
2 | using Microsoft.Extensions.Options;
3 |
4 | namespace Configuration.Controllers
5 | {
6 | [Route("[controller]")]
7 | public class ConfigController : Controller
8 | {
9 | private readonly IOptions _config;
10 |
11 | public ConfigController(IOptions config)
12 | {
13 | _config = config;
14 | }
15 |
16 | public IActionResult Get()
17 | {
18 | return Ok("Config: " + _config.Value.Setting);
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/samples/Configuration/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using CondenserDotNet.Client;
3 | using CondenserDotNet.Configuration;
4 | using CondenserDotNet.Configuration.Consul;
5 | using Microsoft.AspNetCore.Hosting;
6 | using Microsoft.Extensions.DependencyInjection;
7 | using Microsoft.Extensions.Options;
8 |
9 | namespace Configuration
10 | {
11 | public class Program
12 | {
13 | public static void Main(string[] args)
14 | {
15 | var port = ServiceManagerConfig.GetNextAvailablePort();
16 |
17 | //This setup would be done outside of this sample.
18 | //The environment variable is passed to the startup to bootstrap
19 | var environment = "Org1";
20 | Environment
21 | .SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", environment);
22 |
23 | var registry = CondenserConfigBuilder
24 | .FromConsul()
25 | .WithKeysStoredAsJson()
26 | .Build();
27 |
28 | //***Add some config
29 | var config = new ConsulConfig
30 | {
31 | Setting = "Test"
32 | };
33 |
34 | registry.SetKeyJsonAsync($"{environment}/ConsulConfig", config).Wait();
35 | //***
36 |
37 |
38 | registry.AddUpdatingPathAsync(environment).Wait();
39 |
40 | var host = new WebHostBuilder()
41 | .UseKestrel()
42 | .UseUrls($"http://*:{port}")
43 | .ConfigureServices(services =>
44 | {
45 | services.AddSingleton(registry);
46 | services.Configure(opts => opts.ServicePort = port);
47 | })
48 | .UseStartup()
49 | .Build();
50 |
51 | host.Run();
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/samples/Configuration/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyConfiguration("")]
9 | [assembly: AssemblyCompany("")]
10 | [assembly: AssemblyProduct("Configuration")]
11 | [assembly: AssemblyTrademark("")]
12 |
13 | // Setting ComVisible to false makes the types in this assembly not visible
14 | // to COM components. If you need to access a type in this assembly from
15 | // COM, set the ComVisible attribute to true on that type.
16 | [assembly: ComVisible(false)]
17 |
18 | // The following GUID is for the ID of the typelib if this project is exposed to COM
19 | [assembly: Guid("c4224d81-0a55-4693-8184-4d00c8bb5e3b")]
20 |
--------------------------------------------------------------------------------
/samples/Configuration/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:56603/",
7 | "sslPort": 0
8 | }
9 | },
10 | "profiles": {
11 | "Configuration": {
12 | "commandName": "Project"
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/samples/Configuration/Startup.cs:
--------------------------------------------------------------------------------
1 | using CondenserDotNet.Configuration;
2 | using Microsoft.AspNetCore.Builder;
3 | using Microsoft.AspNetCore.Hosting;
4 | using Microsoft.Extensions.DependencyInjection;
5 |
6 | namespace Configuration
7 | {
8 | public class Startup
9 | {
10 | private readonly IConfigurationRegistry _registry;
11 |
12 | public Startup(IConfigurationRegistry registry)
13 | {
14 | _registry = registry;
15 | }
16 |
17 |
18 | public void Configure(IApplicationBuilder app, IHostingEnvironment env)
19 | {
20 | app.UseMvcWithDefaultRoute();
21 | }
22 |
23 | public void ConfigureServices(IServiceCollection services)
24 | {
25 | services.ConfigureReloadable(_registry);
26 |
27 | services.AddOptions()
28 | .AddRouting()
29 | .AddMvc();
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/samples/PocWebsocketsSupport/PocWebsocketsSupport.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | PocWebsocketSupport
5 | PocWebsocketSupport
6 | netcoreapp2.0
7 | Exe
8 | true
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/samples/PocWebsocketsSupport/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using CondenserDotNet.Middleware.WindowsAuthentication;
3 | using Microsoft.AspNetCore.Hosting;
4 |
5 | namespace PocWebsocketsSupport
6 | {
7 | class Program
8 | {
9 | static void Main(string[] args)
10 | {
11 | if(args[0] == "pipe")
12 | {
13 | Startup.UsePipes = true;
14 | }
15 | var host = new WebHostBuilder()
16 | .UseKestrel( options =>
17 | {
18 | //options.UseWindowsAuthentication();
19 | })
20 | .UseUrls($"*://10.0.76.1:50000")
21 | .UseStartup()
22 | .Build();
23 |
24 | host.Run();
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/samples/PocWebsocketsSupport/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "PocWebsocketsSupport": {
4 | "commandName": "Project",
5 | "commandLineArgs": "pipe"
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/samples/PocWebsocketsSupport/Startup.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO.Pipelines;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using CondenserDotNet.Core;
7 | using CondenserDotNet.Core.Routing;
8 | using CondenserDotNet.Server;
9 | using CondenserDotNet.Server.RoutingTrie;
10 | using CondenserDotNet.Middleware.WindowsAuthentication;
11 | using Microsoft.AspNetCore.Builder;
12 | using Microsoft.AspNetCore.Http;
13 | using Microsoft.AspNetCore.Http.Features;
14 | using Microsoft.Extensions.DependencyInjection;
15 | using Microsoft.Extensions.Logging;
16 | using CondenserDotNet.Middleware.Pipelines;
17 |
18 | namespace PocWebsocketsSupport
19 | {
20 | public class Startup
21 | {
22 | public static bool UsePipes { get; internal set; }
23 |
24 | public void ConfigureServices(IServiceCollection services)
25 | {
26 | services.AddCondenser();
27 | services.AddSingleton(new PipeFactory());
28 | if (UsePipes)
29 | {
30 | services.AddTransient();
31 | services.AddSingleton>(x => x.GetService);
32 | }
33 | }
34 |
35 | public void Configure(IApplicationBuilder app, ILoggerFactory logger)
36 | {
37 | //logger.AddConsole(LogLevel.Information, true);
38 | //app.UseWindowsAuthentication();
39 | app.UseCondenser();
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/samples/ProtocolSwitchingTest/GlobalSuppressions.cs:
--------------------------------------------------------------------------------
1 |
2 | // This file is used by Code Analysis to maintain SuppressMessage
3 | // attributes that are applied to this project.
4 | // Project-level suppressions either have no target or are given
5 | // a specific target and scoped to a namespace, type, member, etc.
6 |
7 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Sonar Code Smell", "S1172:Unused method parameters should be removed", Justification = "", Scope = "member", Target = "~M:ProtocolSwitchingTest.Program.Main(System.String[])")]
8 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Sonar Code Smell", "S1118:Utility classes should not have public constructors", Justification = "", Scope = "type", Target = "~T:ProtocolSwitchingTest.Program")]
9 |
10 |
--------------------------------------------------------------------------------
/samples/ProtocolSwitchingTest/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using CondenserDotNet.Middleware.ProtocolSwitcher;
3 | using Microsoft.AspNetCore.Hosting;
4 |
5 | namespace ProtocolSwitchingTest
6 | {
7 | class Program
8 | {
9 | static void Main(string[] args)
10 | {
11 | var host = new WebHostBuilder()
12 | .UseKestrel(options =>
13 | {
14 | options.Switcheroo();
15 | options.UseHttps("testCert.pfx", "testPassword");
16 | })
17 | .UseUrls($"*://*:5000")
18 | .UseStartup()
19 | .Build();
20 |
21 | host.Run();
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/samples/ProtocolSwitchingTest/ProtocolSwitchingTest.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.0
5 | ProtocolSwitchingTest
6 | Exe
7 | ProtocolSwitchingTest
8 | false
9 | false
10 | false
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | PreserveNewest
30 |
31 |
32 |
--------------------------------------------------------------------------------
/samples/ProtocolSwitchingTest/Startup.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using Microsoft.AspNetCore.Builder;
5 | using Microsoft.AspNetCore.Http;
6 | using Microsoft.Extensions.DependencyInjection;
7 |
8 | namespace ProtocolSwitchingTest
9 | {
10 | public class Startup
11 | {
12 | public void ConfigureServices(IServiceCollection serviceCollection)
13 | {
14 | serviceCollection.AddMvc();
15 | }
16 |
17 | public void Configure(IApplicationBuilder app)
18 | {
19 | app.UseMvcWithDefaultRoute();
20 |
21 | }
22 |
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/samples/ProtocolSwitchingTest/TestController.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 |
7 | namespace ProtocolSwitchingTest
8 | {
9 | [Route("[controller]")]
10 | public class TestController:Controller
11 | {
12 | [HttpGet]
13 | public IEnumerable Get()
14 | {
15 | for (int i = 0; i < 1000; i++)
16 | {
17 | var newResponse = new TestResponse()
18 | {
19 | SomeProperty = i.ToString()
20 | };
21 | yield return newResponse;
22 | }
23 | }
24 | }
25 |
26 | public class TestResponse
27 | {
28 | public string SomeProperty { get; set; }
29 | }
30 |
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/samples/Routing/MyMiddleware.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Microsoft.AspNetCore.Http;
3 |
4 | namespace Routing
5 | {
6 | public class MyMiddleware
7 | {
8 | private readonly RequestDelegate _next;
9 |
10 | public MyMiddleware(RequestDelegate next)
11 | {
12 | _next = next;
13 | }
14 |
15 | public async Task Invoke(HttpContext httpContext)
16 | {
17 | await _next(httpContext);
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/samples/Routing/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net.Http;
3 | using CondenserDotNet.Server.DataContracts;
4 | using CondenserDotNet.Server.Extensions;
5 | using Microsoft.AspNetCore.Hosting;
6 | using Microsoft.AspNetCore.Server.Kestrel;
7 | using Microsoft.AspNetCore.Server.Kestrel.Filter;
8 | using Microsoft.Extensions.Logging;
9 |
10 | namespace Routing
11 | {
12 | public partial class Program
13 | {
14 | public static void Main(string[] args)
15 | {
16 | var host = new WebHostBuilder()
17 | .UseKestrel()
18 | .UseUrls($"*://*:{50000}")
19 | .UseStartup()
20 | .Build();
21 |
22 | host.Run();
23 |
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/samples/Routing/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyConfiguration("")]
9 | [assembly: AssemblyCompany("")]
10 | [assembly: AssemblyProduct("Routing")]
11 | [assembly: AssemblyTrademark("")]
12 |
13 | // Setting ComVisible to false makes the types in this assembly not visible
14 | // to COM components. If you need to access a type in this assembly from
15 | // COM, set the ComVisible attribute to true on that type.
16 | [assembly: ComVisible(false)]
17 |
18 | // The following GUID is for the ID of the typelib if this project is exposed to COM
19 | [assembly: Guid("bd511c62-b3f6-4652-9c46-a59be8f3ef99")]
20 |
--------------------------------------------------------------------------------
/samples/Routing/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:56606/",
7 | "sslPort": 0
8 | }
9 | },
10 | "profiles": {
11 | "Routing": {
12 | "commandName": "Project"
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/samples/Routing/Routing.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.0
5 | Routing
6 | Exe
7 | Routing
8 | false
9 | false
10 | false
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/samples/Routing/Startup.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Net.Http;
4 | using System.Text;
5 | using CondenserDotNet.Server;
6 | using CondenserDotNet.Server.DataContracts;
7 | using Microsoft.Extensions.DependencyInjection;
8 | using Microsoft.Extensions.Logging;
9 |
10 | namespace Routing
11 | {
12 | public class Startup
13 | {
14 | public void Configure(ILoggerFactory logger)
15 | {
16 | logger.AddConsole(LogLevel.Trace);
17 | }
18 |
19 | public void ConfigureServices(IServiceCollection services)
20 | {
21 | services.AddCondenserWithBuilder()
22 | .WithHealthRoute("/condenser/health")
23 | .WithHealthCheck(() => new HealthCheck
24 | {
25 | Name = "Default",
26 | Ok = true
27 | })
28 | .WithHttpClient(serviceId => new HttpClient
29 | {
30 | Timeout = TimeSpan.FromSeconds(30)
31 |
32 | }).Build();
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/samples/RoutingWithWindowsAuth/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using CondenserDotNet.Server.DataContracts;
6 | using CondenserDotNet.Server.Extensions;
7 | using CondenserDotNet.Middleware.WindowsAuthentication;
8 | using Microsoft.AspNetCore.Hosting;
9 | using Microsoft.Extensions.Logging;
10 |
11 | namespace RoutingWithWindowsAuth
12 | {
13 | public class Program
14 | {
15 | public static void Main(string[] args)
16 | {
17 | var host = new WebHostBuilder()
18 | .UseKestrel((ops) =>
19 | {
20 | ops.UseWindowsAuthentication();
21 | })
22 | .UseUrls($"http://*:{50000}")
23 | .UseStartup()
24 | .Build();
25 |
26 | host.Run();
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/samples/RoutingWithWindowsAuth/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyConfiguration("")]
9 | [assembly: AssemblyCompany("")]
10 | [assembly: AssemblyProduct("RoutingWithWindowsAuth")]
11 | [assembly: AssemblyTrademark("")]
12 |
13 | // Setting ComVisible to false makes the types in this assembly not visible
14 | // to COM components. If you need to access a type in this assembly from
15 | // COM, set the ComVisible attribute to true on that type.
16 | [assembly: ComVisible(false)]
17 |
18 | // The following GUID is for the ID of the typelib if this project is exposed to COM
19 | [assembly: Guid("e6e9e36f-1a81-4c1d-8313-abd21bc4a980")]
20 |
--------------------------------------------------------------------------------
/samples/RoutingWithWindowsAuth/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:56604/",
7 | "sslPort": 0
8 | }
9 | },
10 | "profiles": {
11 | "RoutingWithWindowsAuth": {
12 | "commandName": "Project"
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/samples/RoutingWithWindowsAuth/RoutingWithWindowsAuth.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.0
5 | RoutingWithWindowsAuth
6 | Exe
7 | RoutingWithWindowsAuth
8 | false
9 | false
10 | false
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/samples/RoutingWithWindowsAuth/Startup.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using CondenserDotNet.Server;
5 | using CondenserDotNet.Server.DataContracts;
6 | using Microsoft.Extensions.DependencyInjection;
7 | using Microsoft.Extensions.Logging;
8 | using Microsoft.AspNetCore.Builder;
9 | using Microsoft.AspNetCore.Http;
10 |
11 | namespace RoutingWithWindowsAuth
12 | {
13 | public class Startup
14 | {
15 | public void Configure(IApplicationBuilder app, ILoggerFactory logger)
16 | {
17 | app.UseMiddleware();
18 | app.Use(async (context, next) =>
19 | {
20 | await context.Response.WriteAsync(context.User.Identity.Name);
21 | return;
22 | });
23 |
24 | logger.AddConsole().AddDebug(LogLevel.Trace);
25 |
26 | }
27 |
28 | public void ConfigureServices(IServiceCollection services)
29 | {
30 | //services.AddCondenserWithBuilder()
31 | // .WithHealthRoute("/condenser/health")
32 | // .WithHealthCheck(() => new HealthCheck
33 | // {
34 | // Name = "Default",
35 | // Ok = true
36 | // }).Build();
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/samples/ServiceRegistration/Controllers/HealthController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using CondenserDotNet.Client.Services;
6 | using Microsoft.AspNetCore.Mvc;
7 |
8 | namespace ServiceRegistration.Controllers
9 | {
10 | [Route("[controller]")]
11 | public class HealthController:Controller
12 | {
13 | private IServiceRegistry _registry;
14 |
15 | public HealthController(IServiceRegistry registry)
16 | {
17 | _registry = registry;
18 | }
19 |
20 | public IActionResult Get()
21 | {
22 | var instance = _registry.GetServiceInstanceAsync("TestService");
23 | instance.Wait();
24 | if (instance.Result != null)
25 | {
26 | return Ok(instance.Result.Service);
27 | }
28 | else
29 | {
30 | return Ok();
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/samples/ServiceRegistration/Controllers/SomeController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using CondenserDotNet.Client.Services;
4 | using CondenserDotNet.Middleware.TrailingHeaders;
5 | using Microsoft.AspNetCore.Mvc;
6 |
7 | namespace ServiceRegistration.Controllers
8 | {
9 | [Route("test")]
10 | public class SomeController : Controller
11 | {
12 | private IServiceRegistry _registry;
13 |
14 | public SomeController(IServiceRegistry registry)
15 | {
16 | _registry = registry;
17 | }
18 |
19 | [HttpGet()]
20 | public IActionResult GetOther()
21 | {
22 | var instance =_registry.GetServiceInstanceAsync("ServiceRegistration");
23 | instance.Wait();
24 | return Ok(instance.Result.Service);
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/samples/ServiceRegistration/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Net;
5 | using System.Net.Http;
6 | using System.Threading.Tasks;
7 | using CondenserDotNet.Client;
8 | using Microsoft.AspNetCore.Hosting;
9 |
10 | namespace ServiceRegistration
11 | {
12 | public class Program
13 | {
14 | public static void Main(string[] args)
15 | {
16 | var config = new CondenserDotNet.Configuration.Consul.ConsulRegistry();
17 | var ignore = config.AddUpdatingPathAsync("/allkeys");
18 |
19 | Console.ReadLine();
20 |
21 | var port = 5000;// ServiceManagerConfig.GetNextAvailablePort();
22 |
23 | var host = new WebHostBuilder()
24 | .UseKestrel()
25 | .UseUrls($"http://*:{port}")
26 | .UseStartup()
27 | .Build();
28 |
29 | host.Run();
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/samples/ServiceRegistration/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyConfiguration("")]
9 | [assembly: AssemblyCompany("")]
10 | [assembly: AssemblyProduct("ServiceRegistration")]
11 | [assembly: AssemblyTrademark("")]
12 |
13 | // Setting ComVisible to false makes the types in this assembly not visible
14 | // to COM components. If you need to access a type in this assembly from
15 | // COM, set the ComVisible attribute to true on that type.
16 | [assembly: ComVisible(false)]
17 |
18 | // The following GUID is for the ID of the typelib if this project is exposed to COM
19 | [assembly: Guid("763c29ce-f23c-496b-8dc4-d60e5399cfd4")]
20 |
--------------------------------------------------------------------------------
/samples/ServiceRegistration/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:56605/",
7 | "sslPort": 0
8 | }
9 | },
10 | "profiles": {
11 | "ServiceRegistration": {
12 | "commandName": "Project"
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/samples/ServiceRegistration/ServiceRegistration.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.0
5 | ServiceRegistration
6 | Exe
7 | ServiceRegistration
8 | false
9 | false
10 | false
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/samples/ServiceRegistration/Startup.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using CondenserDotNet.Client;
6 | using CondenserDotNet.Middleware.TrailingHeaders;
7 | using Microsoft.AspNetCore.Builder;
8 | using Microsoft.AspNetCore.Hosting.Server;
9 | using Microsoft.Extensions.DependencyInjection;
10 | using Microsoft.Extensions.Options;
11 |
12 | namespace ServiceRegistration
13 | {
14 | public class Startup
15 | {
16 | public void ConfigureServices(IServiceCollection services)
17 | {
18 | services.AddMvc();
19 | services.AddConsulServices();
20 | }
21 |
22 | public void Configure(IApplicationBuilder app, IServiceManager manager)
23 | {
24 | manager.AddHttpHealthCheck("health", 10).RegisterServiceAsync();
25 | var secondOps = Options.Create(new ServiceManagerConfig()
26 | {
27 | ServiceName = "TestService",
28 | ServicePort = 5000,
29 | });
30 | var manager2 = new ServiceManager(secondOps).AddHttpHealthCheck("health", 10).RegisterServiceAsync();
31 | app.UseMvcWithDefaultRoute();
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/samples/WebsocketClient/GlobalSuppressions.cs:
--------------------------------------------------------------------------------
1 |
2 | // This file is used by Code Analysis to maintain SuppressMessage
3 | // attributes that are applied to this project.
4 | // Project-level suppressions either have no target or are given
5 | // a specific target and scoped to a namespace, type, member, etc.
6 |
7 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Sonar Code Smell", "S1118:Utility classes should not have public constructors", Justification = "", Scope = "type", Target = "~T:WebsocketClient.Program")]
8 |
9 |
--------------------------------------------------------------------------------
/samples/WebsocketClient/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 | using System.Threading;
4 |
5 | namespace WebsocketClient
6 | {
7 | class Program
8 | {
9 | static void Main(string[] args)
10 | {
11 | Console.WriteLine("Hello World!");
12 | var socket = new System.Net.WebSockets.ClientWebSocket();
13 | var token = new CancellationToken(false);
14 | socket.ConnectAsync(new Uri("ws://localhost:50000/testsample/test3/test2"),token).Wait();
15 | socket.SendAsync(new ArraySegment(Encoding.UTF8.GetBytes("heelo")),System.Net.WebSockets.WebSocketMessageType.Binary,false,token).Wait();
16 | var buffer = new ArraySegment(new byte[1024]);
17 | var result = socket.ReceiveAsync(buffer, token);
18 | result.Wait();
19 | Console.WriteLine(Encoding.UTF8.GetString(buffer.Array,0, result.Result.Count));
20 | Console.ReadLine();
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/samples/WebsocketClient/WebsocketClient.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp2.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/samples/WebsocketSampleServer/Controllers/HealthController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using Microsoft.AspNetCore.Mvc;
5 |
6 | namespace WebsocketSampleServer.Controllers
7 | {
8 | [Route("[controller]")]
9 | public class HealthController : Controller
10 | {
11 | public IActionResult Get()
12 | {
13 | return Ok("Test");
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/samples/WebsocketSampleServer/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Hosting;
2 |
3 | namespace WebsocketSampleServer
4 | {
5 | class Program
6 | {
7 | static void Main(string[] args)
8 | {
9 | var host = new WebHostBuilder()
10 | .UseKestrel()
11 | .UseUrls($"*://*:2222")
12 | .UseStartup()
13 | .Build();
14 |
15 | host.Run();
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/samples/WebsocketSampleServer/WebsocketSampleServer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp2.0
6 | false
7 | false
8 | false
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/script/runtests.bat:
--------------------------------------------------------------------------------
1 | dotnet test -c Debug test/CondenserTests/CondenserTests.csproj
2 | dotnet test -c Debug test/Condenser.Tests.Integration/Condenser.Tests.Integration.csproj
--------------------------------------------------------------------------------
/src/CondenserDotNet.Client/CondenserDotNet.Client.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | CondenserDotNet.Client
6 | CondenserDotNet.Client
7 |
8 |
9 | latest
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Client/DataContracts/HealthCheck.cs:
--------------------------------------------------------------------------------
1 | namespace CondenserDotNet.Client.DataContracts
2 | {
3 | public class HealthCheck
4 | {
5 | public string HTTP { get; set; }
6 | public string Interval { get; set; }
7 | public string TTL { get; set; }
8 | public string Name { get; set; }
9 | public string DeregisterCriticalServiceAfter { get; set; }
10 | public bool tls_skip_verify { get; set; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Client/DataContracts/Service.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace CondenserDotNet.Client.DataContracts
4 | {
5 | public class Service
6 | {
7 | public string ID { get; set; }
8 | public string Name { get; set; }
9 | public List Tags { get; set; }
10 | public string Address { get; set; }
11 | public int Port { get; set; }
12 | public bool EnableTagOverride { get; set; }
13 | public List Checks { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Client/DataContracts/SessionCreate.cs:
--------------------------------------------------------------------------------
1 | namespace CondenserDotNet.Client.DataContracts
2 | {
3 | public class SessionCreate
4 | {
5 | public string LockDelay { get; set; }
6 | public string Name { get; set; }
7 | public string[] Checks { get; set; }
8 | public string Behavior { get; set; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Client/DataContracts/SessionCreateResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CondenserDotNet.Client.DataContracts
4 | {
5 | public class SessionCreateResponse
6 | {
7 | public Guid Id { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Client/GlobalSuppressions.cs:
--------------------------------------------------------------------------------
1 |
2 | // This file is used by Code Analysis to maintain SuppressMessage
3 | // attributes that are applied to this project.
4 | // Project-level suppressions either have no target or are given
5 | // a specific target and scoped to a namespace, type, member, etc.
6 |
7 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "", Scope = "member", Target = "~P:CondenserDotNet.Client.DataContracts.HealthCheck.tls_skip_verify")]
8 |
9 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Client/HealthConfiguration.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using CondenserDotNet.Client.DataContracts;
3 |
4 |
5 | namespace CondenserDotNet.Client
6 | {
7 | public class HealthConfiguration
8 | {
9 | public bool IgnoreTls { get; set; } = true;
10 | public string Url { get; set; }
11 | public int IntervalInSeconds { get; set; } = 30;
12 |
13 | public HealthCheck Build(IServiceManager manager)
14 | {
15 | if (Url == null) return null;
16 | if (!Uri.TryCreate(Url, UriKind.Absolute, out var uri))
17 | {
18 | var scheme = manager.ProtocolSchemeTag ?? "http";
19 | var builder = new UriBuilder(scheme, manager.ServiceAddress, manager.ServicePort, Url);
20 | uri = builder.Uri;
21 | }
22 |
23 | var check = new HealthCheck
24 | {
25 | HTTP = uri.ToString(),
26 | Interval = $"{IntervalInSeconds}s",
27 | Name = $"{manager.ServiceId}:HttpCheck",
28 | tls_skip_verify = IgnoreTls
29 | };
30 |
31 | return check;
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Client/IServiceManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Net.Http;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 | using CondenserDotNet.Client.DataContracts;
7 | using Microsoft.Extensions.Logging;
8 |
9 | namespace CondenserDotNet.Client
10 | {
11 | public interface IServiceManager : IDisposable
12 | {
13 | string ServiceId { get; }
14 | Task RegistrationTask { get; }
15 | string ServiceName { get; }
16 | TimeSpan DeregisterIfCriticalAfter { get; set; }
17 | bool IsRegistered { get; }
18 | ITtlCheck TtlCheck { get; set; }
19 | string ServiceAddress { get; }
20 | int ServicePort { get; }
21 | CancellationToken Cancelled { get; }
22 | HttpClient Client { get; }
23 | List SupportedUrls { get; }
24 | List CustomTags { get; }
25 | HealthConfiguration HealthConfig { get; }
26 | Service RegisteredService { get; set; }
27 | string ProtocolSchemeTag { get; set; }
28 | ILogger Logger { get; }
29 |
30 | bool UpdateRegistrationTask(Task inboundTask);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Client/ITtlCheck.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using CondenserDotNet.Client.DataContracts;
3 |
4 | namespace CondenserDotNet.Client
5 | {
6 | public interface ITtlCheck
7 | {
8 | Task ReportPassingAsync();
9 | Task ReportWarningAsync();
10 | Task ReportFailAsync();
11 | HealthCheck HealthCheck { get; }
12 | }
13 | }
--------------------------------------------------------------------------------
/src/CondenserDotNet.Client/Leadership/ILeaderRegistry.cs:
--------------------------------------------------------------------------------
1 |
2 | using System.Threading.Tasks;
3 |
4 | namespace CondenserDotNet.Client.Leadership
5 | {
6 | public interface ILeaderRegistry
7 | {
8 | ///
9 | /// Creates a session and starts watching the key
10 | ///
11 | /// The full path to the leadership key
12 | ///
13 | Task GetLeaderWatcherAsync(string keyForLeadership);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Client/Leadership/ILeaderWatcher.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using CondenserDotNet.Core.DataContracts;
3 | using System;
4 |
5 | namespace CondenserDotNet.Client.Leadership
6 | {
7 | public interface ILeaderWatcher
8 | {
9 | Task GetCurrentLeaderAsync();
10 | Task GetLeadershipAsync();
11 | void SetLeaderCallback(Action callback);
12 | }
13 | }
--------------------------------------------------------------------------------
/src/CondenserDotNet.Client/Leadership/LeaderRegistry.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 |
5 | namespace CondenserDotNet.Client.Leadership
6 | {
7 | public class LeaderRegistry : ILeaderRegistry
8 | {
9 | private readonly IServiceManager _serviceManager;
10 | private readonly Dictionary _leaderWatchers = new Dictionary(StringComparer.OrdinalIgnoreCase);
11 |
12 | public LeaderRegistry(IServiceManager serviceManager) => _serviceManager = serviceManager;
13 |
14 | public async Task GetLeaderWatcherAsync(string keyForLeadership)
15 | {
16 | var registrationTask = _serviceManager.RegistrationTask;
17 | if(registrationTask == null)
18 | {
19 | throw new InvalidOperationException($"You need to register your service before you can apply for leadership locks lock attempted {keyForLeadership}");
20 | }
21 | await registrationTask.ConfigureAwait(false);
22 | lock (_leaderWatchers)
23 | {
24 | if (!_leaderWatchers.TryGetValue(keyForLeadership, out var returnValue))
25 | {
26 | returnValue = new LeaderWatcher(_serviceManager, keyForLeadership);
27 | _leaderWatchers[keyForLeadership] = returnValue;
28 | }
29 | return returnValue;
30 | }
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Client/MaintenanceExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Net.Http;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace CondenserDotNet.Client
8 | {
9 | public static class MaintenanceExtensions
10 | {
11 | private const string _url = "/v1/agent/service/maintenance/";
12 |
13 | public static Task EnableMaintenanceModeAsync(this IServiceManager manager, string reason)
14 | {
15 | var url = _url + $"{manager.ServiceId}?enable=true&reason={Uri.EscapeDataString(reason)}";
16 | return manager.Client.PutAsync(url, null);
17 | }
18 |
19 | public static Task DisableMaintenanceModeAsync(this IServiceManager manager)
20 | {
21 | var url = _url + $"{manager.ServiceId}?enable=false";
22 | return manager.Client.PutAsync(url, new StringContent(string.Empty));
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Client/ServiceCollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net.Http;
3 | using System.Security.Cryptography;
4 | using System.Security.Cryptography.X509Certificates;
5 | using CondenserDotNet.Client.Leadership;
6 | using CondenserDotNet.Client.Services;
7 | using CondenserDotNet.Core;
8 | using CondenserDotNet.Core.Consul;
9 | using Microsoft.Extensions.DependencyInjection;
10 |
11 | namespace CondenserDotNet.Client
12 | {
13 | public static class ServiceCollectionExtensions
14 | {
15 | public static IServiceCollection AddConsulServices(this IServiceCollection self)
16 | {
17 | self.AddSingleton();
18 | self.AddSingleton();
19 | self.AddSingleton();
20 | self.AddTransient();
21 | self.AddTransient();
22 | return self;
23 | }
24 |
25 | public static IServiceCollection AddConsulServices(this IServiceCollection self, string aclToken)
26 | {
27 | self.AddSingleton(new StaticConsulAclProvider(aclToken));
28 | return self.AddConsulServices();
29 | }
30 |
31 | public static IServiceCollection AddConsulServices(this IServiceCollection self, string encryptedAclTokenKey, X509Certificate2 encryptionCertifcate, RSAEncryptionPadding padding)
32 | {
33 | self.AddSingleton(new EncryptedConsulKeyAclProvider(encryptedAclTokenKey, encryptionCertifcate, padding));
34 | return self.AddConsulServices();
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Client/ServiceManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Net.Http;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 | using CondenserDotNet.Client.DataContracts;
7 | using CondenserDotNet.Core;
8 | using CondenserDotNet.Core.Consul;
9 | using Microsoft.AspNetCore.Hosting.Server;
10 | using Microsoft.Extensions.Logging;
11 | using Microsoft.Extensions.Options;
12 |
13 | namespace CondenserDotNet.Client
14 | {
15 | public class ServiceManager : IServiceManager
16 | {
17 | private readonly List _supportedUrls = new List();
18 | private readonly List _customTags = new List();
19 | private readonly CancellationTokenSource _cancel = new CancellationTokenSource();
20 | private bool _disposed;
21 | private ITtlCheck _ttlCheck;
22 | private Task _registrationTask;
23 |
24 | public ServiceManager(IOptions optionsConfig, Func httpClientFactory = null, ILoggerFactory logFactory = null, IServer server = null, IConsulAclProvider aclProvider = null)
25 | {
26 | if (optionsConfig.Value.ServicePort == 0 && server == null)
27 | {
28 | throw new ArgumentOutOfRangeException($"A valid server port needs to be set through either the options or the hosting server");
29 | }
30 |
31 | var config = optionsConfig.Value;
32 | Logger = logFactory?.CreateLogger();
33 | Client = httpClientFactory?.Invoke() ?? HttpUtils.CreateClient(aclProvider);
34 | config.SetDefaults(server);
35 | ServiceId = config.ServiceId;
36 | ServiceName = config.ServiceName;
37 | ServiceAddress = config.ServiceAddress;
38 | ServicePort = config.ServicePort;
39 | }
40 |
41 | public List SupportedUrls => _supportedUrls;
42 | public List CustomTags => _customTags;
43 | public ILogger Logger { get; }
44 | public HttpClient Client { get; }
45 | public HealthConfiguration HealthConfig { get; private set; } = new HealthConfiguration();
46 | public Service RegisteredService { get; set; }
47 | public string ServiceId { get; }
48 | public string ServiceName { get; }
49 | public TimeSpan DeregisterIfCriticalAfter { get; set; }
50 | public bool IsRegistered => RegisteredService != null;
51 | public ITtlCheck TtlCheck { get => _ttlCheck; set => _ttlCheck = value; }
52 | public string ServiceAddress { get; }
53 | public int ServicePort { get; }
54 | public CancellationToken Cancelled => _cancel.Token;
55 | public string ProtocolSchemeTag { get; set; }
56 | public Task RegistrationTask => Volatile.Read(ref _registrationTask);
57 |
58 | public void Dispose()
59 | {
60 | Dispose(true);
61 | GC.SuppressFinalize(this);
62 | }
63 |
64 | protected void Dispose(bool disposing)
65 | {
66 | if (_disposed) return;
67 | try
68 | {
69 | _cancel.Cancel();
70 | }
71 | finally
72 | {
73 | Client.Dispose();
74 | _disposed = true;
75 | }
76 | }
77 |
78 | public bool UpdateRegistrationTask(Task inboundTask)
79 | {
80 | var originalValue = Interlocked.Exchange(ref _registrationTask, inboundTask);
81 | return originalValue == null ? true : false;
82 | }
83 |
84 | ~ServiceManager() => Dispose(false);
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Client/ServiceManagerConfig.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 | using System.Net;
4 | using System.Net.Sockets;
5 | using Microsoft.AspNetCore.Hosting.Server;
6 | using Microsoft.AspNetCore.Hosting.Server.Features;
7 | using System.Linq;
8 |
9 | namespace CondenserDotNet.Client
10 | {
11 | public class ServiceManagerConfig
12 | {
13 | public string ServiceName { get; set; }
14 | public string ServiceId { get; set; }
15 | public string ServiceAddress { get; set; }
16 | public int ServicePort { get; set; }
17 | public string TagForScheme { get; set; } = "Protocol";
18 | public Tuple[] PortAndSchemes { get; set; }
19 | public string[] EndsToStrip { get; set; } = { ".Host", ".Library", ".Service" };
20 | public bool UseMultipleProtocols { get; set; } = true;
21 |
22 | public static int GetNextAvailablePort()
23 | {
24 | var l = new TcpListener(IPAddress.Loopback, 0);
25 | var port = 0;
26 | try
27 | {
28 | l.Start();
29 | port = ((IPEndPoint)l.LocalEndpoint).Port;
30 | l.Stop();
31 | l.Server.Dispose();
32 | }
33 | catch { /*Nom nom */}
34 | return port;
35 | }
36 |
37 | internal void SetDefaults(IServer server)
38 | {
39 | if (string.IsNullOrWhiteSpace(ServiceName))
40 | {
41 | var assemblyName = System.IO.Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location);
42 | foreach (var strip in EndsToStrip ?? new string[0])
43 | {
44 | if (assemblyName.EndsWith(strip))
45 | {
46 | assemblyName = assemblyName.Substring(0, assemblyName.Length - strip.Length);
47 | }
48 | }
49 | ServiceName = assemblyName;
50 | }
51 | if (string.IsNullOrWhiteSpace(ServiceAddress))
52 | {
53 | var hostName = Dns.GetHostEntryAsync(string.Empty);
54 | hostName.Wait();
55 | var name = hostName.Result.HostName;
56 | ServiceAddress = name;
57 | }
58 | if (string.IsNullOrWhiteSpace(ServiceId))
59 | {
60 | ServiceId = $"{ServiceName}:{Dns.GetHostName()}";
61 | }
62 | if (ServicePort == 0)
63 | {
64 | var feature = server.Features.Get();
65 | var add = feature.Addresses.First().Replace("*", "test");
66 | ServicePort = new Uri(add).Port;
67 | }
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Client/Services/IServiceRegistry.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using CondenserDotNet.Core.DataContracts;
5 |
6 | namespace CondenserDotNet.Client.Services
7 | {
8 | public interface IServiceRegistry
9 | {
10 | Task> GetAvailableServicesAsync();
11 | Task> GetAvailableServicesWithTagsAsync();
12 | Task GetServiceInstanceAsync(string serviceName);
13 | Task GetNearestServiceInstanceAsync(string serviceName);
14 | ServiceBasedHttpHandler GetHttpHandler();
15 | ServiceBasedHttpHandler GetHttpHandler(int maxConnectionsPerServer);
16 | WatcherState GetServiceCurrentState(string serviceName);
17 | void SetServiceListCallback(string serviceName, Action> callback);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Client/Services/NoConsulConnectionException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CondenserDotNet.Client.Services
4 | {
5 | public class NoConsulConnectionException : Exception
6 | {
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Client/Services/NoServiceInstanceFoundException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CondenserDotNet.Client.Services
4 | {
5 | public class NoServiceInstanceFoundException : Exception
6 | {
7 | public NoServiceInstanceFoundException(string serviceName, Exception innerException)
8 | : base($"Unable to find an instance of the service {serviceName}", innerException) => ServiceName = serviceName;
9 |
10 | public string ServiceName { get; }
11 | public override string ToString() => Message;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Client/Services/ServiceBasedHttpHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net.Http;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 |
6 | namespace CondenserDotNet.Client.Services
7 | {
8 | public class ServiceBasedHttpHandler : HttpClientHandler
9 | {
10 | private readonly IServiceRegistry _serviceRegistry;
11 |
12 | public ServiceBasedHttpHandler(IServiceRegistry serviceRegistry, int maxConnectionsPerServer)
13 | {
14 | _serviceRegistry = serviceRegistry;
15 | MaxConnectionsPerServer = maxConnectionsPerServer;
16 | }
17 |
18 | protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
19 | {
20 | var currentUri = request.RequestUri;
21 | var serviceInstance = await _serviceRegistry.GetServiceInstanceAsync(currentUri.Host).ConfigureAwait(false);
22 | if (serviceInstance == null)
23 | {
24 | throw new NoServiceInstanceFoundException(currentUri.Host, null);
25 | }
26 | request.RequestUri = new Uri($"{currentUri.Scheme}://{serviceInstance.Address}:{serviceInstance.Port}{currentUri.PathAndQuery}");
27 | return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Client/Services/ServiceRegistryDelegatingHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Net.Http;
4 | using System.Text;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 |
8 | namespace CondenserDotNet.Client.Services
9 | {
10 | public class ServiceRegistryDelegatingHandler : DelegatingHandler
11 | {
12 | private readonly IServiceRegistry _serviceRegistry;
13 |
14 | public ServiceRegistryDelegatingHandler(IServiceRegistry serviceRegistry) => _serviceRegistry = serviceRegistry;
15 |
16 | protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
17 | {
18 | var currentUri = request.RequestUri;
19 | if (System.Net.IPAddress.TryParse(currentUri.Host, out _)) return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
20 | var serviceInstance = await _serviceRegistry.GetServiceInstanceAsync(currentUri.Host).ConfigureAwait(false);
21 | if (serviceInstance == null)
22 | {
23 | throw new NoServiceInstanceFoundException(currentUri.Host, null);
24 | }
25 | request.RequestUri = new Uri($"{currentUri.Scheme}://{serviceInstance.Address}:{serviceInstance.Port}{currentUri.PathAndQuery}");
26 | return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Client/Services/ServiceRegistryNearestDelegatingHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Net.Http;
4 | using System.Text;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 |
8 | namespace CondenserDotNet.Client.Services
9 | {
10 | public class ServiceRegistryNearestDelegatingHandler : DelegatingHandler
11 | {
12 | private readonly IServiceRegistry _serviceRegistry;
13 |
14 | public ServiceRegistryNearestDelegatingHandler(IServiceRegistry serviceRegistry) => _serviceRegistry = serviceRegistry;
15 |
16 | protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
17 | {
18 | var currentUri = request.RequestUri;
19 | if (System.Net.IPAddress.TryParse(currentUri.Host, out _)) return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
20 | var serviceInstance = await _serviceRegistry.GetNearestServiceInstanceAsync(currentUri.Host).ConfigureAwait(false);
21 | if (serviceInstance == null)
22 | {
23 | throw new NoServiceInstanceFoundException(currentUri.Host, null);
24 | }
25 | request.RequestUri = new Uri($"{currentUri.Scheme}://{serviceInstance.Address}:{serviceInstance.Port}{currentUri.PathAndQuery}");
26 | return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Client/Services/WatcherState.cs:
--------------------------------------------------------------------------------
1 | namespace CondenserDotNet.Client.Services
2 | {
3 | public enum WatcherState
4 | {
5 | NotInitialized,
6 | UsingCachedValues,
7 | UsingLiveValues
8 | }
9 | }
--------------------------------------------------------------------------------
/src/CondenserDotNet.Client/TtlCheck.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using CondenserDotNet.Client.DataContracts;
3 |
4 | namespace CondenserDotNet.Client
5 | {
6 | public class TtlCheck : ITtlCheck
7 | {
8 | private readonly IServiceManager _parentManager;
9 | private readonly HealthCheck _healthCheck;
10 |
11 | internal TtlCheck(IServiceManager parentManager, int timeToLiveSeconds)
12 | {
13 | _parentManager = parentManager;
14 | _healthCheck = new HealthCheck()
15 | {
16 | TTL = $"{timeToLiveSeconds}s"
17 | };
18 | }
19 |
20 | public HealthCheck HealthCheck => _healthCheck;
21 |
22 | public async Task ReportPassingAsync()
23 | {
24 | if (!_parentManager.IsRegistered)
25 | {
26 | return false;
27 | }
28 | //We are connected so lets report to the server
29 | var response = await _parentManager.Client.PutAsync($"/v1/agent/check/pass/service:{_parentManager.ServiceId}", null).ConfigureAwait(false);
30 | if (response.IsSuccessStatusCode)
31 | {
32 | return true;
33 | }
34 | return false;
35 | }
36 | public async Task ReportWarningAsync()
37 | {
38 | if (!_parentManager.IsRegistered)
39 | {
40 | return false;
41 | }
42 | //We are connected so lets report to the server
43 | var response = await _parentManager.Client.PutAsync($"/v1/agent/check/warn/service:{_parentManager.ServiceId}", null).ConfigureAwait(false);
44 | if (response.IsSuccessStatusCode)
45 | {
46 | return true;
47 | }
48 | return false;
49 | }
50 | public async Task ReportFailAsync()
51 | {
52 | if (!_parentManager.IsRegistered)
53 | {
54 | return false;
55 | }
56 | //We are connected so lets report to the server
57 | var response = await _parentManager.Client.PutAsync($"/v1/agent/check/fail/service:{_parentManager.ServiceId}", null).ConfigureAwait(false);
58 | if (response.IsSuccessStatusCode)
59 | {
60 | return true;
61 | }
62 | return false;
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Configuration/CondenserConfigBuilder.cs:
--------------------------------------------------------------------------------
1 | using CondenserDotNet.Configuration.Consul;
2 | using Microsoft.Extensions.Options;
3 |
4 | namespace CondenserDotNet.Configuration
5 | {
6 | public class CondenserConfigBuilder
7 | {
8 | private readonly ConsulRegistryConfig _config = new ConsulRegistryConfig();
9 |
10 | public CondenserConfigBuilder()
11 | {
12 | }
13 |
14 | public static CondenserConfigBuilder FromConsul() => new CondenserConfigBuilder();
15 |
16 | public CondenserConfigBuilder WithKeysStoredAsJson()
17 | {
18 | _config.KeyParser = new JsonKeyValueParser();
19 | return this;
20 | }
21 |
22 | public CondenserConfigBuilder WithAgentPort(int port)
23 | {
24 | _config.AgentPort = port;
25 | return this;
26 | }
27 |
28 | public CondenserConfigBuilder WithAgentAddress(string address)
29 | {
30 | _config.AgentAddress = address;
31 | return this;
32 | }
33 |
34 |
35 | public IConfigurationRegistry Build()
36 | {
37 | var options = Options.Create(_config);
38 | return new ConsulRegistry(options);
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Configuration/CondenserDotNet.Configuration.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 | latest
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Configuration/ConfigurationBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 |
3 | namespace CondenserDotNet.Configuration
4 | {
5 | public static class ConfigurationBuilderExtensions
6 | {
7 | public static IConfigurationBuilder AddConfigurationRegistry(this IConfigurationBuilder self, IConfigurationRegistry registry)
8 | {
9 | var consul = new ConfigurationRegistrySource(registry);
10 | return self.Add(consul);
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/src/CondenserDotNet.Configuration/ConfigurationRegistryExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Newtonsoft.Json;
3 |
4 | namespace CondenserDotNet.Configuration
5 | {
6 | public static class ConfigurationRegistryExtensions
7 | {
8 | public static Task SetKeyJsonAsync(this IConfigurationRegistry self, string key, T value) => self.SetKeyAsync(key, JsonConvert.SerializeObject(value));
9 | }
10 | }
--------------------------------------------------------------------------------
/src/CondenserDotNet.Configuration/ConfigurationRegistryProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Microsoft.Extensions.Configuration;
5 |
6 | namespace CondenserDotNet.Configuration
7 | {
8 | public class ConfigurationRegistryProvider : ConfigurationProvider
9 | {
10 | private readonly IConfigurationRegistry _configurationRegistry;
11 |
12 | public ConfigurationRegistryProvider(IConfigurationRegistry configurationRegistry)
13 | {
14 | _configurationRegistry = configurationRegistry;
15 | _configurationRegistry.AddWatchOnEntireConfig(Load);
16 | }
17 |
18 | public override bool TryGet(string key, out string value) => _configurationRegistry.TryGetValue(key, out value);
19 | public override void Set(string key, string value) => _configurationRegistry.SetKeyAsync(key, value).Wait();
20 | public override void Load() => OnReload();
21 |
22 | public override IEnumerable GetChildKeys(IEnumerable earlierKeys, string parentPath)
23 | {
24 | var prefix = parentPath == null ? string.Empty : parentPath + ConfigurationPath.KeyDelimiter;
25 |
26 | //Need to override this as we are not setting base Data, so expose all keys on registry
27 | return _configurationRegistry.AllKeys
28 | .Where(key => key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
29 | .Select(key => Segment(key, prefix.Length))
30 | .Concat(earlierKeys)
31 | .OrderBy(key => key, ConfigurationKeyComparer.Instance);
32 | }
33 |
34 | private static string Segment(string key, int prefixLength)
35 | {
36 | var indexOf = key.IndexOf(ConfigurationPath.KeyDelimiter, prefixLength, StringComparison.OrdinalIgnoreCase);
37 | return indexOf < 0 ? key.Substring(prefixLength) : key.Substring(prefixLength, indexOf - prefixLength);
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/src/CondenserDotNet.Configuration/ConfigurationRegistrySource.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 |
3 | namespace CondenserDotNet.Configuration
4 | {
5 | public class ConfigurationRegistrySource : IConfigurationSource
6 | {
7 | private readonly IConfigurationRegistry _configurationRegistry;
8 |
9 | public ConfigurationRegistrySource(IConfigurationRegistry configurationRegistry) => _configurationRegistry = configurationRegistry;
10 |
11 | public IConfigurationProvider Build(IConfigurationBuilder builder) => new ConfigurationRegistryProvider(_configurationRegistry);
12 | }
13 | }
--------------------------------------------------------------------------------
/src/CondenserDotNet.Configuration/Consul/ConfigurationWatcher.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CondenserDotNet.Configuration.Consul
4 | {
5 | internal class ConfigurationWatcher
6 | {
7 | public string CurrentValue { get; set; }
8 | public Action CallBack { get; set; }
9 | public Action CallbackAllKeys { get; set; }
10 | public string KeyToWatch { get; set; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Configuration/Consul/ConsulRegistryConfig.cs:
--------------------------------------------------------------------------------
1 | namespace CondenserDotNet.Configuration.Consul
2 | {
3 | public class ConsulRegistryConfig
4 | {
5 | public string AgentAddress { get; set; } = "localhost";
6 | public int AgentPort { get; set; } = 8500;
7 | public IKeyParser KeyParser { get; set; } = new SimpleKeyValueParser();
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Configuration/Consul/IConfigSource.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading.Tasks;
3 |
4 | namespace CondenserDotNet.Configuration.Consul
5 | {
6 | public interface IConfigSource
7 | {
8 | object CreateWatchState();
9 | string FormValidKey(string keyPath, bool dontPreifxStart = false);
10 | Task GetKeysAsync(string keyPath);
11 | Task TryWatchKeysAsync(string keyPath, object state);
12 | Task<(bool found, string value)> GetKeyAsync(string keyPath);
13 | Task TrySetKeyAsync(string keyPath, string value);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Configuration/Consul/IKeyParser.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace CondenserDotNet.Configuration.Consul
4 | {
5 | public interface IKeyParser
6 | {
7 | IEnumerable Parse(KeyValue key);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Configuration/Consul/KeyOperationResult.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CondenserDotNet.Configuration.Consul
6 | {
7 | public struct KeyOperationResult
8 | {
9 | public bool Success;
10 | public Dictionary Dictionary;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Configuration/Consul/KeyValue.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 |
4 | namespace CondenserDotNet.Configuration.Consul
5 | {
6 | public class KeyValue
7 | {
8 | public string Key { get; set; }
9 | public string Value { get; set; }
10 | public string Session { get; set; }
11 | public bool IsDerivedKey { get; set; }
12 |
13 | public string ValueFromBase64() => Encoding.UTF8.GetString(Convert.FromBase64String(Value));
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Configuration/Consul/SimpleKeyValueParser.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace CondenserDotNet.Configuration.Consul
4 | {
5 | internal class SimpleKeyValueParser : IKeyParser
6 | {
7 | public static readonly SimpleKeyValueParser Instance = new SimpleKeyValueParser();
8 |
9 | public IEnumerable Parse(KeyValue key)
10 | {
11 | yield return key;
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/src/CondenserDotNet.Configuration/IConfigurationRegistry.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Threading.Tasks;
5 | using Microsoft.Extensions.Configuration;
6 |
7 | namespace CondenserDotNet.Configuration
8 | {
9 | public interface IConfigurationRegistry : IDisposable
10 | {
11 | string this[string key] { get; }
12 | Task AddStaticKeyPathAsync(string keyPath);
13 |
14 | Task AddStaticKeyPathAsync(string keyPath, bool singleKey = false);
15 |
16 | Task AddUpdatingPathAsync(string keyPath);
17 | void AddWatchOnEntireConfig(Action callback);
18 | void AddWatchOnSingleKey(string keyToWatch, Action callback);
19 | Task SetKeyAsync(string keyPath, string value);
20 | bool TryGetValue(string key, out string value);
21 | IEnumerable AllKeys { get; }
22 | IConfigurationRoot Root { get; }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Configuration/ServiceCollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 | using Microsoft.Extensions.DependencyInjection;
3 |
4 | namespace CondenserDotNet.Configuration
5 | {
6 | public static class ServiceCollectionExtensions
7 | {
8 | public static IServiceCollection ConfigureReloadable(this IServiceCollection self,
9 | IConfigurationRegistry registry)
10 | where TConfig : class => self.ConfigureReloadable(registry.Root, registry, typeof(TConfig).Name);
11 |
12 | public static IServiceCollection ConfigureReloadable(this IServiceCollection self,
13 | IConfigurationRegistry registry, string sectionName)
14 | where TConfig : class => self.ConfigureReloadable(registry.Root, registry, sectionName);
15 |
16 |
17 | public static IServiceCollection ConfigureReloadable(this IServiceCollection self,
18 | IConfiguration configuration, IConfigurationRegistry registry)
19 | where TConfig : class => self.ConfigureReloadable(configuration, registry, typeof(TConfig).Name);
20 |
21 | public static IServiceCollection ConfigureReloadable(this IServiceCollection self,
22 | IConfiguration configuration, IConfigurationRegistry registry, string sectionName)
23 | where TConfig : class
24 | {
25 | var initialised = false;
26 | self.Configure
27 | (config =>
28 | {
29 | void Bind()
30 | {
31 | var section = configuration.GetSection(sectionName);
32 | section.Bind(config);
33 | }
34 |
35 | if (!initialised)
36 | {
37 | registry.AddWatchOnEntireConfig(Bind);
38 | initialised = true;
39 | }
40 | Bind();
41 | });
42 |
43 | return self;
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Core/AsyncManualResetEvent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | namespace CondenserDotNet.Core
6 | {
7 | public class AsyncManualResetEvent where T : IEquatable
8 | {
9 | private volatile TaskCompletionSource m_tcs = new TaskCompletionSource();
10 |
11 | public Task WaitAsync() => m_tcs.Task;
12 |
13 | public void Set(T result)
14 | {
15 | while (true)
16 | {
17 | if (!m_tcs.TrySetResult(result) && !m_tcs.Task.Result.Equals(result))
18 | {
19 | Interlocked.Exchange(ref m_tcs, new TaskCompletionSource());
20 | }
21 | else
22 | {
23 | return;
24 | }
25 | }
26 | }
27 |
28 | public void Reset()
29 | {
30 | while (true)
31 | {
32 | var tcs = m_tcs;
33 | if (!tcs.Task.IsCompleted || Interlocked.CompareExchange(ref m_tcs, new TaskCompletionSource(), tcs) == tcs) return;
34 | }
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Core/CondenserDotNet.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | CondenserDotNet.Core
6 | CondenserDotNet.Core
7 |
8 |
9 | latest
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Core/CondenserEventSource.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics.Tracing;
4 | using System.Text;
5 |
6 | namespace CondenserDotNet.Core
7 | {
8 | [EventSource(Name = "Condenser-Event-Source")]
9 | public sealed class CondenserEventSource:EventSource
10 | {
11 | public static readonly CondenserEventSource Log = new CondenserEventSource();
12 |
13 | [Event(1, Message = "Service-Watcher-Created")]
14 | public void ServiceWatcherCreated(string serviceName) => Log.WriteEvent(1, serviceName);
15 |
16 | [Event(2, Message = "Http-Client-Created")]
17 | public void HttpClientCreated() => Log.WriteEvent(2);
18 |
19 | [Event(3, Message = "Leadership-Session-Created")]
20 | public void LeadershipSessionCreated() => Log.WriteEvent(3);
21 |
22 | [Event(4, Message = "Leadership-Get-Status")]
23 | public void LeadershipSessionGetStatus(string keyPath) => Log.WriteEvent(4, keyPath);
24 |
25 | [Event(5, Message = "Leadership-Try-To-Lock")]
26 | public void LeadershipTryToLock(string keyPath) => Log.WriteEvent(5, keyPath);
27 |
28 | [Event(6, Message = "Configuration-HttpClient-Created")]
29 | public void ConfigurationHttpClientCreated() => Log.WriteEvent(6);
30 |
31 | [Event(7, Message = "Configuration-Get-Keys-Recursive")]
32 | public void ConfigurationGetKeysRecursive(string keyPath) => Log.WriteEvent(7, keyPath);
33 |
34 | [Event(8, Message = "Configuration-Get-Key")]
35 | public void ConfigurationGetKey(string keyPath) => Log.WriteEvent(8, keyPath);
36 |
37 | [Event(9, Message = "Configuration-Watch-Key")]
38 | public void ConfigurationWatchKey(string keyPath) => Log.WriteEvent(9, keyPath);
39 |
40 | [Event(10, Message = "Configuration-Set-Key")]
41 | public void ConfigurationSetKey(string keyPath) => Log.WriteEvent(10, keyPath);
42 |
43 | [Event(11, Message = "Service-Registration")]
44 | public void ServiceRegistration() => Log.WriteEvent(11);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Core/Consul/EncryptedConsulKeyAclProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Security.Cryptography;
4 | using System.Security.Cryptography.X509Certificates;
5 | using System.Text;
6 | using Newtonsoft.Json;
7 |
8 | namespace CondenserDotNet.Core.Consul
9 | {
10 | public class EncryptedConsulKeyAclProvider : IConsulAclProvider
11 | {
12 | private readonly string _aclToken;
13 |
14 | public EncryptedConsulKeyAclProvider(string encryptedKeyName, X509Certificate2 encryptionCertifcate, RSAEncryptionPadding padding)
15 | {
16 | if (!encryptionCertifcate.HasPrivateKey) throw new ArgumentException("Certificate needs to have the private key to decrypt");
17 | if (encryptedKeyName.StartsWith("/")) encryptedKeyName = encryptedKeyName.Substring(1);
18 | using (var httpClient = HttpUtils.CreateClient(null))
19 | {
20 | var keyValues = httpClient.GetStringAsync($"/v1/kv/{encryptedKeyName}").Result;
21 | var keys = JsonConvert.DeserializeObject(keyValues);
22 | if (keys.Length != 1) throw new ArgumentException($"Should only be a single key returned from query but had {keys.Length}");
23 | var keyValue = Encoding.UTF8.GetString(Convert.FromBase64String(keys[0].Value));
24 | var decryptedValue = encryptionCertifcate.GetRSAPrivateKey().Decrypt(Convert.FromBase64String(keyValue), padding);
25 | _aclToken = Encoding.UTF8.GetString(decryptedValue);
26 | }
27 | }
28 |
29 | public string GetAclToken() => _aclToken;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Core/Consul/IConsulAclProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CondenserDotNet.Core.Consul
6 | {
7 | public interface IConsulAclProvider
8 | {
9 | string GetAclToken();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Core/Consul/KeyValue.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 |
4 | namespace CondenserDotNet.Core.Consul
5 | {
6 | public class KeyValue
7 | {
8 | public string Key { get; set; }
9 | public string Value { get; set; }
10 | public string Session { get; set; }
11 | public bool IsDerivedKey { get; set; }
12 |
13 | public string ValueFromBase64() => Encoding.UTF8.GetString(Convert.FromBase64String(Value));
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Core/Consul/StaticConsulAclProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CondenserDotNet.Core.Consul
6 | {
7 | public class StaticConsulAclProvider : IConsulAclProvider
8 | {
9 | private readonly string _aclToken;
10 |
11 | public StaticConsulAclProvider(string aclToken) => _aclToken = aclToken;
12 |
13 | public string GetAclToken() => _aclToken;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Core/DataContracts/InformationCheck.cs:
--------------------------------------------------------------------------------
1 | namespace CondenserDotNet.Core.DataContracts
2 | {
3 | public class InformationCheck
4 | {
5 | public string CheckID { get; set; }
6 | public string Name { get; set; }
7 | public string Status { get; set; }
8 | public string Output { get; set; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Core/DataContracts/InformationNode.cs:
--------------------------------------------------------------------------------
1 | namespace CondenserDotNet.Core.DataContracts
2 | {
3 | public class InformationNode
4 | {
5 | public string Node { get; set; }
6 | public string Address { get; set; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Core/DataContracts/InformationService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CondenserDotNet.Core.DataContracts
4 | {
5 | public class InformationService : IEquatable
6 | {
7 | public string ID { get; set; }
8 | public string Service { get; set; }
9 | public string[] Tags { get; set; }
10 | public string Address { get; set; }
11 | public int Port { get; set; }
12 |
13 | public bool Equals(InformationService other)
14 | {
15 | if (Address != other.Address) return false;
16 | if (Port != other.Port) return false;
17 | if (Service != other.Service) return false;
18 | if (ID != other.ID) return false;
19 | return true;
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Core/DataContracts/InformationServiceSet.cs:
--------------------------------------------------------------------------------
1 | namespace CondenserDotNet.Core.DataContracts
2 | {
3 | public class InformationServiceSet
4 | {
5 | public InformationNode Node { get; set; }
6 | public InformationService Service { get; set; }
7 | public InformationCheck[] Checks { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Core/RandHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Threading;
5 |
6 | namespace CondenserDotNet.Core
7 | {
8 | public static class RandHelper
9 | {
10 | private static int _seed = Environment.TickCount;
11 | private static readonly ThreadLocal s_random =
12 | new ThreadLocal(() => new Random(Interlocked.Increment(ref _seed)));
13 |
14 | public static int Next(int lowerBound, int upperBound) => s_random.Value.Next(lowerBound, upperBound);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Core/Routing/DefaultRouting.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Threading;
4 |
5 | namespace CondenserDotNet.Core.Routing
6 | {
7 | public class DefaultRouting : IDefaultRouting
8 | {
9 | IRoutingStrategy _default;
10 | public DefaultRouting(IEnumerable> strategy, IRoutingConfig config)
11 | {
12 | var name = (config?.DefaultRouteStrategy ?? RouteStrategy.Random.ToString());
13 | SetDefault(strategy.Single(x => x.Name == name));
14 | }
15 |
16 | public IRoutingStrategy Default => _default;
17 | public void SetDefault(IRoutingStrategy strategy) => Interlocked.Exchange(ref _default, strategy);
18 | }
19 | }
--------------------------------------------------------------------------------
/src/CondenserDotNet.Core/Routing/IDefaultRouting.cs:
--------------------------------------------------------------------------------
1 | namespace CondenserDotNet.Core.Routing
2 | {
3 | public interface IDefaultRouting
4 | {
5 | IRoutingStrategy Default { get; }
6 | void SetDefault(IRoutingStrategy strateg);
7 | }
8 | }
--------------------------------------------------------------------------------
/src/CondenserDotNet.Core/Routing/IRoutingConfig.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CondenserDotNet.Core.Routing
4 | {
5 | public interface IRoutingConfig
6 | {
7 | string DefaultRouteStrategy { get; }
8 | Action OnRoutesBuilt { get; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Core/Routing/IRoutingStrategy.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace CondenserDotNet.Core.Routing
4 | {
5 | public interface IRoutingStrategy
6 | {
7 | T RouteTo(List services);
8 | string Name { get; }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/CondenserDotNet.Core/Routing/RandomRouteNode.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace CondenserDotNet.Core.Routing
4 | {
5 | public class RandomRoutingStrategy : IRoutingStrategy
6 | {
7 | public static RandomRoutingStrategy Default { get; } = new RandomRoutingStrategy();
8 |
9 | public T RouteTo(List instances) => instances?.Count > 0 ? instances[RandHelper.Next(0, instances.Count)] : default;
10 | public string Name { get; } = RouteStrategy.Random.ToString();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Core/Routing/RoundRobinRoutingStrategy.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading;
3 |
4 | namespace CondenserDotNet.Core.Routing
5 | {
6 | public class RoundRobinRoutingStrategy : IRoutingStrategy
7 | {
8 | private int _index = -1;
9 |
10 | public T RouteTo(List instances)
11 | {
12 | if (instances?.Count > 0)
13 | {
14 | var index = Interlocked.Increment(ref _index);
15 | return instances[index % instances.Count];
16 | }
17 | return default;
18 | }
19 |
20 | public string Name { get; } = RouteStrategy.RoundRobin.ToString();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Core/Routing/RouteStrategy.cs:
--------------------------------------------------------------------------------
1 | namespace CondenserDotNet.Core.Routing
2 | {
3 | public enum RouteStrategy
4 | {
5 | RoundRobin,
6 | Random
7 | }
8 | }
--------------------------------------------------------------------------------
/src/CondenserDotNet.Core/Routing/UseTopRouting.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CondenserDotNet.Core.Routing
6 | {
7 | public class UseTopRouting : IRoutingStrategy
8 | {
9 | public static UseTopRouting Default { get; } = new UseTopRouting();
10 |
11 | public string Name => "UseTopRouting";
12 |
13 | public T RouteTo(List services) => services?.Count > 0 ? services[0] : default;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Core/ServiceUtils.cs:
--------------------------------------------------------------------------------
1 | namespace CondenserDotNet.Core
2 | {
3 | public static class ServiceUtils
4 | {
5 | private const string UrlPrefix = "urlprefix-";
6 |
7 | public static string[] RoutesFromTags(string[] tags)
8 | {
9 | var returnCount = 0;
10 | for (var i = 0; i < tags.Length; i++)
11 | {
12 | if (!tags[i].StartsWith(UrlPrefix))
13 | {
14 | continue;
15 | }
16 | returnCount++;
17 | }
18 | var returnValues = new string[returnCount];
19 | returnCount = 0;
20 | for (var i = 0; i < tags.Length; i++)
21 | {
22 | if (!tags[i].StartsWith(UrlPrefix))
23 | {
24 | continue;
25 | }
26 | var startSubstIndex = UrlPrefix.Length;
27 | var endSubstIndex = tags[i].Length - UrlPrefix.Length;
28 | if (tags[i][tags[i].Length - 1] == '/')
29 | {
30 | endSubstIndex--;
31 | }
32 | returnValues[returnCount] = tags[i].Substring(startSubstIndex, endSubstIndex);
33 | if (returnValues[returnCount][0] != '/')
34 | {
35 | returnValues[returnCount] = "/" + returnValues[returnCount];
36 | }
37 | returnCount++;
38 | }
39 | return returnValues;
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Middleware/CleanShutdown/CleanShutdownExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 | using Microsoft.AspNetCore.Hosting;
3 | using Microsoft.Extensions.DependencyInjection;
4 |
5 | namespace CondenserDotNet.Middleware.CleanShutdown
6 | {
7 | public static class CleanShutdownExtensions
8 | {
9 | public static IServiceCollection AddCleanShutdown(this IServiceCollection serviceCollection)
10 | {
11 | serviceCollection.AddSingleton();
12 | return serviceCollection;
13 | }
14 |
15 | public static IApplicationBuilder UseCleanShutdown(this IApplicationBuilder appBuilder)
16 | {
17 | var appLifetime = appBuilder.ApplicationServices.GetService();
18 | var shutdownService = appBuilder.ApplicationServices.GetService();
19 | appLifetime.ApplicationStopping.Register(() => shutdownService.Shutdown());
20 | appBuilder.UseMiddleware();
21 | return appBuilder;
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Middleware/CleanShutdown/CleanShutdownMiddleware.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 | using System.Threading.Tasks;
3 |
4 | namespace CondenserDotNet.Middleware.CleanShutdown
5 | {
6 | public class CleanShutdownMiddleware
7 | {
8 | private RequestDelegate _next;
9 | private CleanShutdownService _shutdownService;
10 |
11 | public CleanShutdownMiddleware(RequestDelegate next, CleanShutdownService shutdownService)
12 | {
13 | _next = next;
14 | _shutdownService = shutdownService;
15 | }
16 |
17 | public async Task Invoke(HttpContext httpContext)
18 | {
19 | _shutdownService.StartRequest();
20 | try
21 | {
22 | await _next.Invoke(httpContext).ConfigureAwait(false);
23 | }
24 | finally
25 | {
26 | _shutdownService.FinishRequest();
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Middleware/CleanShutdown/CleanShutdownService.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 | using System.Threading;
3 |
4 | namespace CondenserDotNet.Middleware.CleanShutdown
5 | {
6 | public class CleanShutdownService
7 | {
8 | private readonly CountdownEvent _requestsOutstanding = new CountdownEvent(1);
9 | private readonly int _shutdownTimeout = 2000;
10 | private readonly ILogger _logger;
11 |
12 | public CleanShutdownService(ILoggerFactory loggerFactory) => _logger = loggerFactory.CreateLogger();
13 | public void StartRequest() => _requestsOutstanding.AddCount();
14 | public void FinishRequest() => _requestsOutstanding.Signal();
15 |
16 | public void Shutdown()
17 | {
18 | _requestsOutstanding.Signal();
19 | if (!_requestsOutstanding.Wait(_shutdownTimeout))
20 | {
21 | _logger?.LogWarning($"Could not perform a clean shutdown there were {_requestsOutstanding.CurrentCount} remaining when the {_shutdownTimeout}ms timeout expired");
22 | }
23 | else
24 | {
25 | _logger?.LogInformation("Clean shutdown completed succesfully");
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Middleware/CondenserDotNet.Middleware.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | CondenserDotNet.Middleware
6 | CondenserDotNet.Middleware
7 | true
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Middleware/ConsulCleanShutdown/ConsulShutdownExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using CondenserDotNet.Middleware.CleanShutdown;
5 | using Microsoft.AspNetCore.Builder;
6 | using Microsoft.AspNetCore.Hosting;
7 | using Microsoft.Extensions.DependencyInjection;
8 |
9 | namespace CondenserDotNet.Middleware.ConsulCleanShutdown
10 | {
11 | public static class ConsulShutdownExtensions
12 | {
13 | public static IServiceCollection AddConsulShutdown(this IServiceCollection serviceCollection)
14 | {
15 | serviceCollection.AddSingleton();
16 | return serviceCollection.AddCleanShutdown();
17 | }
18 |
19 | public static IApplicationBuilder UseConsulShutdown(this IApplicationBuilder appBuilder, string shutdownMessage)
20 | {
21 | var appLifetime = appBuilder.ApplicationServices.GetService();
22 | var shutdownService = appBuilder.ApplicationServices.GetService();
23 | shutdownService.ShutdownMessage = shutdownMessage;
24 | appLifetime.ApplicationStopping.Register(shutdownService.Stopping);
25 | appLifetime.ApplicationStarted.Register(shutdownService.Started);
26 | return appBuilder.UseCleanShutdown();
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Middleware/ConsulCleanShutdown/ConsulShutdownService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using CondenserDotNet.Client;
5 |
6 | namespace CondenserDotNet.Middleware.ConsulCleanShutdown
7 | {
8 | public class ConsulShutdownService
9 | {
10 | private IServiceManager _serviceManager;
11 |
12 | public ConsulShutdownService(IServiceManager serviceManager)
13 | {
14 | _serviceManager = serviceManager;
15 | ShutdownMessage = string.Empty;
16 | }
17 |
18 | public string ShutdownMessage { get; set; }
19 |
20 | public void Stopping() => _serviceManager.EnableMaintenanceModeAsync(ShutdownMessage).Wait();
21 |
22 | public void Started() => _serviceManager.DisableMaintenanceModeAsync().Wait();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Middleware/TrailingHeaders/ChunkingStream.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 | using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
7 |
8 | namespace CondenserDotNet.Middleware.TrailingHeaders
9 | {
10 | public class ChunkingStream : Stream
11 | {
12 | private Stream _innerStream;
13 | private static readonly byte[] _endChunkBytes = Encoding.ASCII.GetBytes("\r\n");
14 |
15 | public override bool CanRead => throw new NotImplementedException();
16 | public override bool CanSeek => throw new NotImplementedException();
17 | public override bool CanWrite => true;
18 | public override long Length => throw new NotImplementedException();
19 | public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
20 | public Stream InnerStream { get => _innerStream; set => _innerStream = value; }
21 | public override void Flush() => _innerStream.Flush();
22 |
23 | public override Task FlushAsync(CancellationToken cancellationToken) => _innerStream.FlushAsync(cancellationToken);
24 | public override int Read(byte[] buffer, int offset, int count) => throw new NotSupportedException();
25 | public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
26 | public override void SetLength(long value) => throw new NotSupportedException();
27 |
28 | public override void Write(byte[] buffer, int offset, int count)
29 | {
30 | var beginChunkBytes = ChunkWriter.BeginChunkBytes(count);
31 |
32 | _innerStream.Write(beginChunkBytes.Array, beginChunkBytes.Offset, beginChunkBytes.Count);
33 | _innerStream.Write(buffer, offset, count);
34 | _innerStream.Write(_endChunkBytes, 0, _endChunkBytes.Length);
35 | }
36 |
37 | public async override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
38 | {
39 | var beginChunkBytes = ChunkWriter.BeginChunkBytes(count);
40 |
41 | await _innerStream.WriteAsync(beginChunkBytes.Array, beginChunkBytes.Offset, beginChunkBytes.Count, cancellationToken);
42 | await _innerStream.WriteAsync(buffer, offset, count, cancellationToken);
43 | await _innerStream.WriteAsync(_endChunkBytes, 0, _endChunkBytes.Length, cancellationToken);
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Middleware/TrailingHeaders/ITrailingHeadersFeature.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CondenserDotNet.Middleware.TrailingHeaders
4 | {
5 | public interface ITrailingHeadersFeature
6 | {
7 | void RegisterHeader(string name, Func contentCallback);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Middleware/TrailingHeaders/TrailingHeadersFeature.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Microsoft.AspNetCore.Http;
5 | using Microsoft.Extensions.Primitives;
6 |
7 | namespace CondenserDotNet.Middleware.TrailingHeaders
8 | {
9 | public class TrailingHeadersFeature : ITrailingHeadersFeature
10 | {
11 | private readonly List>> _headers = new List>>();
12 | private readonly HttpContext _context;
13 |
14 | public TrailingHeadersFeature(HttpContext context) => _context = context;
15 | public List>> Headers => _headers;
16 |
17 | public void RegisterHeader(string name, Func contentCallback)
18 | {
19 | for (var i = 0; i < _headers.Count; i++)
20 | {
21 | var headerItem = _headers[i];
22 | if (string.Compare(name, headerItem.Item1, StringComparison.OrdinalIgnoreCase) == 0)
23 | {
24 | _headers[i] = Tuple.Create>(headerItem.Item1, () => headerItem.Item2() + ", " + contentCallback());
25 | return;
26 | }
27 | }
28 | _headers.Add(Tuple.Create(name, contentCallback));
29 | _context.Response.Headers["Trailer"] = new StringValues(_headers.Select(h => h.Item1).ToArray());
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Middleware/TrailingHeaders/TrailingHeadersMiddleware.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 | using System.Threading.Tasks;
3 | using Microsoft.AspNetCore.Http;
4 |
5 | namespace CondenserDotNet.Middleware.TrailingHeaders
6 | {
7 | public class TrailingHeadersMiddleware
8 | {
9 | private readonly RequestDelegate _next;
10 |
11 | public TrailingHeadersMiddleware(RequestDelegate next) => _next = next;
12 |
13 | public async Task Invoke(HttpContext context)
14 | {
15 | var trailingHeaders = new TrailingHeadersFeature(context);
16 | var stream = new ChunkingStream()
17 | {
18 | InnerStream = context.Response.Body
19 | };
20 | context.Response.Body = stream;
21 | context.Response.Headers["Transfer-Encoding"] = "chunked";
22 | context.Features.Set(trailingHeaders);
23 | await _next(context);
24 | context.Response.Body = stream.InnerStream;
25 | var sb = new StringBuilder();
26 | sb.AppendLine("0");
27 | foreach (var header in trailingHeaders.Headers)
28 | {
29 | sb.Append(header.Item1)
30 | .Append(": ")
31 | .Append(header.Item2())
32 | .AppendLine();
33 | }
34 | sb.AppendLine();
35 | var bytes = Encoding.ASCII.GetBytes(sb.ToString());
36 | await context.Response.Body.WriteAsync(bytes, 0, bytes.Length);
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Middleware/WindowsAuthentication/AuthenticationConnectionMiddleware.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Threading.Tasks;
4 | using Microsoft.AspNetCore.Connections;
5 |
6 | namespace CondenserDotNet.Middleware.WindowsAuthentication
7 | {
8 | public class AuthenticationConnectionMiddleware : ConnectionHandler
9 | {
10 | private ConnectionDelegate _next;
11 |
12 | public AuthenticationConnectionMiddleware(ConnectionDelegate next) => _next = next;
13 |
14 | public override async Task OnConnectedAsync(ConnectionContext connection)
15 | {
16 | using var authFeature = new WindowsAuthFeature();
17 | connection.Features.Set(authFeature);
18 | await _next(connection).ConfigureAwait(false);
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Middleware/WindowsAuthentication/IWindowsAuthFeature.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Principal;
2 |
3 | namespace CondenserDotNet.Middleware.WindowsAuthentication
4 | {
5 | public interface IWindowsAuthFeature
6 | {
7 | WindowsIdentity Identity { get; set; }
8 | WindowsIdentity GetUser();
9 | string ProcessHandshake(string tokenName, byte[] token);
10 | }
11 | }
--------------------------------------------------------------------------------
/src/CondenserDotNet.Middleware/WindowsAuthentication/Interop/Windows/Interop.Libraries.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | internal static partial class Interop
7 | {
8 | internal static partial class Libraries
9 | {
10 | internal const string Secur32 = "Secur32.dll";
11 | internal const string Kernel32 = "kernel32.dll";
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Middleware/WindowsAuthentication/Interop/Windows/Kernel32/Interop.CloseHandle .cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | internal partial class Interop
5 | {
6 | internal partial class Kernel32
7 | {
8 | [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = false)]
9 | internal static extern int CloseHandle(IntPtr handle);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Middleware/WindowsAuthentication/Interop/Windows/Secur32/Interop.AcceptSecurityContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | internal partial class Interop
5 | {
6 | internal partial class Secur32
7 | {
8 | [DllImport(Libraries.Secur32, CharSet = CharSet.Unicode, SetLastError = false)]
9 | internal static extern SEC_RESULT AcceptSecurityContext(SecurityHandle phCredential, ref SecurityHandle phContext,
10 | ref SecBufferDesc pInput, ASC_REQ fContextReq, Data_Rep TargetDataRep, out SecurityHandle phNewContext,
11 | ref SecBufferDesc pOutput, out uint pfContextAttr, out SecurityInteger ptsTimeStamp);
12 | [DllImport(Libraries.Secur32, CharSet = CharSet.Unicode, SetLastError = false)]
13 | internal static extern SEC_RESULT AcceptSecurityContext(SecurityHandle phCredential, IntPtr phContext,
14 | ref SecBufferDesc pInput, ASC_REQ fContextReq, Data_Rep TargetDataRep, out SecurityHandle phNewContext,
15 | ref SecBufferDesc pOutput, out uint pfContextAttr, out SecurityInteger ptsTimeStamp);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Middleware/WindowsAuthentication/Interop/Windows/Secur32/Interop.AcquireCredentialsHandle.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | internal partial class Interop
5 | {
6 | internal partial class Secur32
7 | {
8 | [DllImport(Libraries.Secur32, CharSet = CharSet.Unicode, SetLastError = false)]
9 | public static extern SEC_RESULT AcquireCredentialsHandle(string pszPrincipal, string pszPackage, CredentialsUse fCredentialUse,
10 | IntPtr pvLogonID, IntPtr pAuthData, int pGetKeyFn, IntPtr pvGetKeyArgument, out SecurityHandle phCredential,
11 | out SecurityInteger ptsExpiry);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Middleware/WindowsAuthentication/Interop/Windows/Secur32/Interop.DeleteSecurityContext.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 |
3 | internal partial class Interop
4 | {
5 | internal partial class Secur32
6 | {
7 | [DllImport(Libraries.Secur32, CharSet = CharSet.Unicode, SetLastError = false)]
8 | internal static extern SEC_RESULT DeleteSecurityContext(SecurityHandle phCredential);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Middleware/WindowsAuthentication/Interop/Windows/Secur32/Interop.Flags.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | internal partial class Interop
5 | {
6 | internal partial class Secur32
7 | {
8 | [Flags]
9 | internal enum CredentialsUse : int
10 | {
11 | SECPKG_CRED_INBOUND = 1,
12 | SECPKG_CRED_OUTBOUND = 2,
13 | SECPKG_CRED_BOTH = (SECPKG_CRED_OUTBOUND | SECPKG_CRED_INBOUND),
14 | }
15 |
16 | internal enum Data_Rep : uint
17 | {
18 | SECURITY_NATIVE_DREP = 0x00000010,
19 | SECURITY_NETWORK_DREP = 0x00000000,
20 | }
21 |
22 | internal enum SEC_RESULT : uint
23 | {
24 | SEC_E_INSUFFICIENT_MEMORY = 0x80090300, //The function failed. There is not enough memory available to complete the requested action.
25 | SEC_E_INTERNAL_ERROR = 0x80090304, //The function failed. An error occurred that did not map to an SSPI error code.
26 | SEC_E_INVALID_HANDLE = 0x80100003, //The function failed. The handle passed to the function is not valid.
27 | SEC_E_INVALID_TOKEN = 0x80090308, //The function failed. The token passed to the function is not valid.
28 | SEC_E_LOGON_DENIED = 0x8009030C, //The logon failed.
29 | SEC_E_NO_AUTHENTICATING_AUTHORITY = 0x80090311, //The function failed. No authority could be contacted for authentication. This could be due to the following conditions:
30 | SEC_E_OK = 0x00000000, //The function succeeded. The security context received from the client was accepted. If an output token was generated by the function, it must be sent to the client process.
31 | SEC_I_COMPLETE_AND_CONTINUE = 0x00090314, //The function succeeded. The server must call CompleteAuthToken and pass the output token to the client. The server then waits for a return token from the client and then makes another call to AcceptSecurityContext (NTLM).
32 | SEC_I_COMPLETE_NEEDED = 0x00090313, //The function succeeded. The server must finish building the message from the client and then call the CompleteAuthToken function.
33 | SEC_I_CONTINUE_NEEDED = 0x00090312
34 | }
35 |
36 | [Flags]
37 | internal enum ASC_REQ : uint
38 | {
39 | ASC_REQ_DELEGATE = 0x00000001,
40 | ASC_REQ_MUTUAL_AUTH = 0x00000002,
41 | ASC_REQ_REPLAY_DETECT = 0x00000004,
42 | ASC_REQ_SEQUENCE_DETECT = 0x00000008,
43 | ASC_REQ_CONFIDENTIALITY = 0x00000010,
44 | ASC_REQ_USE_SESSION_KEY = 0x00000020,
45 | ASC_REQ_ALLOCATE_MEMORY = 0x00000100,
46 | ASC_REQ_USE_DCE_STYLE = 0x00000200,
47 | ASC_REQ_DATAGRAM = 0x00000400,
48 | ASC_REQ_CONNECTION = 0x00000800,
49 | ASC_REQ_CALL_LEVEL = 0x00001000,
50 | ASC_REQ_EXTENDED_ERROR = 0x00008000,
51 | ASC_REQ_STREAM = 0x00010000,
52 | ASC_REQ_INTEGRITY = 0x00020000,
53 | ASC_REQ_LICENSING = 0x00040000,
54 | ASC_REQ_IDENTIFY = 0x00080000,
55 | ASC_REQ_ALLOW_NULL_SESSION = 0x00100000,
56 | ASC_REQ_ALLOW_NON_USER_LOGONS = 0x00200000,
57 | ASC_REQ_ALLOW_CONTEXT_REPLAY = 0x00400000,
58 | ASC_REQ_FRAGMENT_TO_FIT = 0x00800000,
59 | ASC_REQ_FRAGMENT_SUPPLIED = 0x00002000,
60 | ASC_REQ_NO_TOKEN = 0x01000000,
61 | ASC_REQ_PROXY_BINDINGS = 0x04000000,
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Middleware/WindowsAuthentication/Interop/Windows/Secur32/Interop.FreeCredentialsHandle.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | internal partial class Interop
5 | {
6 | internal partial class Secur32
7 | {
8 | [DllImport(Libraries.Secur32, CharSet = CharSet.Unicode, SetLastError = false)]
9 | internal static extern SEC_RESULT FreeCredentialsHandle(SecurityHandle phCredential);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Middleware/WindowsAuthentication/Interop/Windows/Secur32/Interop.QuerySecurityContextToken .cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | internal partial class Interop
5 | {
6 | internal partial class Secur32
7 | {
8 | [DllImport(Libraries.Secur32, CharSet = CharSet.Unicode, SetLastError = false)]
9 | internal static extern SEC_RESULT QuerySecurityContextToken(ref SecurityHandle phContext, out IntPtr phToken);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Middleware/WindowsAuthentication/Interop/Windows/Secur32/Interop.SecurityBuffers.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | internal partial class Interop
5 | {
6 | internal partial class Secur32
7 | {
8 | internal enum SecurityBufferType : uint
9 | {
10 | SECBUFFER_VERSION = 0,
11 | SECBUFFER_EMPTY = 0,
12 | SECBUFFER_DATA = 1,
13 | SECBUFFER_TOKEN = 2
14 | }
15 |
16 | [StructLayout(LayoutKind.Sequential)]
17 | internal struct SecBufferDesc
18 | {
19 | public uint ulVersion;
20 | public uint cBuffers;
21 | public IntPtr pBuffers;
22 | }
23 |
24 | [StructLayout(LayoutKind.Sequential)]
25 | internal struct SecBuffer
26 | {
27 | public uint cbBuffer;
28 | public SecurityBufferType BufferType;
29 | public IntPtr pvBuffer;
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Middleware/WindowsAuthentication/Interop/Windows/Secur32/Interop.SecurityHandle.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | internal partial class Interop
5 | {
6 | internal partial class Secur32
7 | {
8 | [StructLayout(LayoutKind.Sequential)]
9 | internal struct SecurityHandle
10 | {
11 | public IntPtr LowPart;
12 | public IntPtr HighPart;
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Middleware/WindowsAuthentication/Interop/Windows/Secur32/Interop.SecurityInteger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | internal partial class Interop
5 | {
6 | internal partial class Secur32
7 | {
8 | [StructLayout(LayoutKind.Sequential)]
9 | internal struct SecurityInteger
10 | {
11 | public uint LowPart;
12 | public int HighPart;
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Middleware/WindowsAuthentication/WindowsAuthFeature.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Security.Principal;
3 | using static Interop.Secur32;
4 |
5 | namespace CondenserDotNet.Middleware.WindowsAuthentication
6 | {
7 | public class WindowsAuthFeature : IDisposable, IWindowsAuthFeature
8 | {
9 | private static SecurityHandle _credentialsHandle;
10 | private WindowsHandshake _handshake;
11 |
12 | static WindowsAuthFeature()
13 | {
14 | var result = AcquireCredentialsHandle(null, "Negotiate", CredentialsUse.SECPKG_CRED_INBOUND,
15 | IntPtr.Zero, IntPtr.Zero, 0, IntPtr.Zero, out _credentialsHandle, out var timeSpan);
16 | if (result != SEC_RESULT.SEC_E_OK)
17 | {
18 | throw new InvalidOperationException();
19 | }
20 | }
21 |
22 | public WindowsAuthFeature() => _handshake = new WindowsHandshake(_credentialsHandle);
23 |
24 | public WindowsIdentity Identity { get; set; }
25 |
26 | public void Dispose()
27 | {
28 | Identity?.Dispose();
29 | _handshake?.Dispose();
30 | Identity = null;
31 | }
32 |
33 | public string ProcessHandshake(string tokenName, byte[] token) => _handshake.AcceptSecurityToken(tokenName, token);
34 |
35 | public WindowsIdentity GetUser()
36 | {
37 | var user = _handshake.User;
38 | if (user == null)
39 | {
40 | return null;
41 | }
42 | _handshake.Dispose();
43 | _handshake = null;
44 | return user;
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Middleware/WindowsAuthentication/WindowsAuthenticationExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 | using Microsoft.AspNetCore.Server.Kestrel;
3 | using Microsoft.AspNetCore.Server.Kestrel.Core;
4 |
5 | namespace CondenserDotNet.Middleware.WindowsAuthentication
6 | {
7 | public static class WindowsAuthenticationExtensions
8 | {
9 | public static ListenOptions UseWindowsAuthentication(this ListenOptions options)
10 | {
11 | options.Use(next =>
12 | {
13 | var middleware = new AuthenticationConnectionMiddleware(next);
14 | return middleware.OnConnectedAsync;
15 | });
16 | return options;
17 | }
18 |
19 | public static IApplicationBuilder UseWindowsAuthentication(this IApplicationBuilder self)
20 | {
21 | self.UseMiddleware();
22 | return self;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Middleware/WindowsAuthentication/WindowsAuthenticationMiddleware.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Net;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Http;
6 | using Microsoft.AspNetCore.Http.Features;
7 | using Microsoft.Extensions.Logging;
8 |
9 | namespace CondenserDotNet.Middleware.WindowsAuthentication
10 | {
11 | public class WindowsAuthenticationMiddleware
12 | {
13 | private const string AuthorizationHeader = "Authorization";
14 | private const string NtlmToken = "NTLM ";
15 | private const string NegotiateToken = "Negotiate ";
16 | private const string WWWAuthenticateHeader = "WWW-Authenticate";
17 | private static readonly string[] _supportedTokens = new[] { "NTLM", "Negotiate" };
18 | private readonly RequestDelegate _next;
19 | private readonly ILogger _logger;
20 | private static readonly Task _cachedTask = Task.FromResult(0);
21 |
22 | public WindowsAuthenticationMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
23 | {
24 | _next = next;
25 | _logger = loggerFactory?.CreateLogger();
26 | }
27 |
28 | public Task Invoke(HttpContext httpContext)
29 | {
30 | var t = httpContext.Features.Get();
31 | var authFeature = httpContext.Features.Get();
32 |
33 | if (authFeature == null)
34 | {
35 | throw new InvalidOperationException("You need the connection filter installed to use windows authentication");
36 | }
37 |
38 | if (authFeature.Identity == null)
39 | {
40 | var sessionId = t.ConnectionId;
41 |
42 | var authorizationHeader = httpContext.Request.Headers[AuthorizationHeader];
43 | var tokenHeader = authorizationHeader.FirstOrDefault(h => h.StartsWith(NtlmToken) || h.StartsWith(NegotiateToken));
44 | if (string.IsNullOrEmpty(tokenHeader))
45 | {
46 | httpContext.Response.Headers.Add(WWWAuthenticateHeader, _supportedTokens);
47 | httpContext.Response.StatusCode = 401;
48 | return _cachedTask;
49 | }
50 | var tokenName = tokenHeader.Substring(0, tokenHeader.IndexOf(' ') + 1);
51 | tokenHeader = tokenHeader.Substring(tokenHeader.IndexOf(' ') + 1);
52 | var token = Convert.FromBase64String(tokenHeader);
53 | string result = null;
54 | try
55 | {
56 | result = authFeature.ProcessHandshake(tokenName, token);
57 | }
58 | catch
59 | {
60 | httpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
61 | }
62 | if (result != null)
63 | {
64 | httpContext.Response.Headers.Add(WWWAuthenticateHeader, new[] { result });
65 | }
66 | var user = authFeature.GetUser();
67 | if (user == null)
68 | {
69 | httpContext.Response.StatusCode = 401;
70 | httpContext.Response.ContentLength = 0;
71 | return _cachedTask;
72 | }
73 | authFeature.Identity = user;
74 | }
75 | httpContext.User = new System.Security.Claims.ClaimsPrincipal(authFeature.Identity);
76 | return _next(httpContext);
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Stats/CondenserDotNet.Stats.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Stats/MetricEntry.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CondenserDotNet.StatsD
6 | {
7 | public struct MetricEntry
8 | {
9 | public string MetricName { get; set; }
10 | public long Value { get; set; }
11 | public MetricType Type { get; set; }
12 | public DateTime MetricTime { get; set; }
13 |
14 | public long UnixTime => (MetricTime - DateTime.UtcNow).Ticks * 100;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Stats/MetricType.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CondenserDotNet.StatsD
6 | {
7 | public enum MetricType
8 | {
9 | Guage,
10 | Counter,
11 | Histogram,
12 | Meter,
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Stats/SocketUdpConnecction.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Net.Sockets;
4 | using System.Text;
5 |
6 | namespace CondenserDotNet.Stats
7 | {
8 | public class SocketUdpConnecction
9 | {
10 | //public SocketUdpConnecction(Socket socket, MemoryPool)
11 | //{
12 |
13 | //}
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Stats/StatsDClient.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Collections.Generic;
4 | using System.Net.Sockets;
5 | using System.Text;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 |
9 | namespace CondenserDotNet.StatsD
10 | {
11 | public abstract class StatsDClient
12 | {
13 | protected readonly SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1);
14 | protected readonly byte[] _buffer;
15 | //protected readonly Memory _remainingBuffer;
16 | protected readonly UdpClient _udpClient;
17 |
18 | private const byte _seperator = 0x3A; // :
19 |
20 | public StatsDClient(int bufferSize) => _buffer = System.Buffers.ArrayPool.Shared.Rent(bufferSize);//_remainingBuffer = _buffer;
21 |
22 | public void SendMetric(MetricEntry metricEntry)
23 | {
24 | //var initialLength = _remainingBuffer.Length;
25 | // var length = Encoding.UTF8.GetByteCount(metricEntry.MetricName) + 1 + ;
26 | //var span = _remainingBuffer.Span;
27 |
28 | //if(span.Length <= length)
29 | //{
30 | // //Need to send the current buffer and retry
31 | // _udpClient.Send()
32 | //}
33 |
34 |
35 | //UTF8Encoding.UTF8.GetBytes(metricEntry.MetricName, )
36 |
37 | //_waitingEntries.Enqueue(metricEntry);
38 | //_semaphoreSlim.Release(1);
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/CondenserDotNet.Stats/UpdStatsD.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace CondenserDotNet.StatsD
7 | {
8 | //public sealed class UpdStatsD : StatsDClient
9 | //{
10 |
11 | //}
12 | }
13 |
--------------------------------------------------------------------------------
/test/Condenser.FullFramework/Condenser.FullFramework.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | exe
5 |
6 |
7 | net461
8 | Condenser.Tests.Integration
9 | Condenser.Tests.Integration
10 | true
11 | false
12 | false
13 | false
14 |
15 | exe
16 |
17 |
18 |
19 | x64
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | PreserveNewest
50 |
51 |
52 | PreserveNewest
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/test/Condenser.FullFramework/SwitcherRooFacts.cs:
--------------------------------------------------------------------------------
1 | using CondenserDotNet.Middleware.ProtocolSwitcher;
2 | using Microsoft.AspNetCore.Builder;
3 | using Microsoft.AspNetCore.Hosting;
4 | using Microsoft.AspNetCore.Http;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Net.Http;
8 | using System.Security.Cryptography.X509Certificates;
9 | using System.Threading.Tasks;
10 | using Xunit;
11 |
12 | namespace Condenser.FullFramework
13 | {
14 | public class SwitcherRooFacts
15 | {
16 | public static X509Certificate2 Certificate = new X509Certificate2(@"TestCert.pfx", "Test123t");
17 |
18 | [Fact(Skip ="")]
19 | public async Task SwitcherooSeesHttpsFact()
20 | {
21 | var port = CondenserDotNet.Client.ServiceManagerConfig.GetNextAvailablePort();
22 | var host = new WebHostBuilder()
23 | .UseKestrel((ops) =>
24 | {
25 | //ops.Switcheroo();
26 | ops.UseHttps(Certificate);
27 | })
28 | .UseUrls($"https://*:{port}")
29 | .UseStartup()
30 | .Build();
31 | host.Start();
32 |
33 | var t = Task.Run(() =>
34 | {
35 | try
36 | {
37 | var client = new HttpClient(new HttpClientHandler()
38 | {
39 | });
40 |
41 | var result = client.GetAsync($"https://localhost:{port}");
42 | result.Wait();
43 | var isHttps = result.Result.Content.ReadAsStringAsync();
44 | isHttps.Wait();
45 | Assert.True(bool.Parse(isHttps.Result));
46 | }
47 | finally
48 | {
49 | host.Dispose();
50 | }
51 | });
52 | await t.ConfigureAwait(false);
53 | }
54 |
55 | [Fact(Skip ="")]
56 | public async Task SwitcherooSeesHttpFact()
57 | {
58 | var port = CondenserDotNet.Client.ServiceManagerConfig.GetNextAvailablePort();
59 | var host = new WebHostBuilder()
60 | .UseKestrel((ops) =>
61 | {
62 | ops.Switcheroo();
63 | ops.UseHttps(Certificate);
64 | })
65 | .UseUrls($"*://*:{port}")
66 | .UseStartup()
67 | .Build();
68 | host.Start();
69 |
70 | try
71 | {
72 | var client = new HttpClient();
73 |
74 | var result = await client.GetAsync($"http://localhost:{port}");
75 | var isHttps = await result.Content.ReadAsStringAsync();
76 | Assert.False(bool.Parse(isHttps));
77 | }
78 | finally
79 | {
80 | host.Dispose();
81 | }
82 | }
83 |
84 | public class Startup
85 | {
86 | public void Configure(IApplicationBuilder app) =>
87 | app.Use(async (context, next) =>
88 | {
89 | await context.Response.WriteAsync(context.Request.IsHttps.ToString());
90 | return;
91 | });
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/test/Condenser.FullFramework/xunit.runner.json:
--------------------------------------------------------------------------------
1 | {
2 | "methodDisplay": "method",
3 | "longRunningTestSeconds": 15
4 | }
--------------------------------------------------------------------------------
/test/Condenser.Tests.Integration/AuthenticationMiddlewareFacts.cs:
--------------------------------------------------------------------------------
1 | using CondenserDotNet.Middleware.WindowsAuthentication;
2 | using Microsoft.AspNetCore.Builder;
3 | using Microsoft.AspNetCore.Hosting;
4 | using Microsoft.AspNetCore.Http;
5 | using System.Net.Http;
6 | using System.Threading.Tasks;
7 | using Xunit;
8 | using Condenser.Tests.Integration.Internal;
9 |
10 | namespace Condenser.Tests.Integration
11 | {
12 | public class AuthenticationMiddlewareFacts
13 | {
14 | [WindowsOnlyFact]
15 | public async Task CanAuthenticateWithNtlm()
16 | {
17 | var host = new WebHostBuilder()
18 | .UseKestrel((ops) =>
19 | {
20 | ops.Listen(System.Net.IPAddress.Any, 55555, lo =>
21 | {
22 | lo.UseWindowsAuthentication();
23 | });
24 | })
25 | .UseUrls($"http://*:{55555}")
26 | .UseStartup()
27 | .Build();
28 | host.Start();
29 |
30 | try
31 | {
32 | var client = new HttpClient(new HttpClientHandler()
33 | {
34 | UseDefaultCredentials = true
35 | });
36 | using (var result = await client.GetAsync($"http://localhost:55555"))
37 | {
38 | var name = await result.Content.ReadAsStringAsync();
39 | Assert.Equal(System.Security.Principal.WindowsIdentity.GetCurrent().Name, name);
40 | }
41 | }
42 | finally
43 | {
44 | host.Dispose();
45 | }
46 | }
47 |
48 | public class Startup
49 | {
50 | public void Configure(IApplicationBuilder app)
51 | {
52 | app.UseMiddleware();
53 | app.Use(async (context, next) =>
54 | {
55 | await context.Response.WriteAsync(context.User.Identity.Name);
56 | return;
57 | });
58 | }
59 | }
60 |
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/test/Condenser.Tests.Integration/Condenser.Tests.Integration.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | Condenser.Tests.Integration
6 | Condenser.Tests.Integration
7 | true
8 | false
9 | false
10 | false
11 | latest
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | PreserveNewest
42 |
43 |
44 | PreserveNewest
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/test/Condenser.Tests.Integration/Internal/WindowsOnlyFact.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 |
3 | namespace Condenser.Tests.Integration.Internal
4 | {
5 | public class WindowsOnlyFact : Xunit.FactAttribute
6 | {
7 | public WindowsOnlyFact()
8 | {
9 | if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
10 | {
11 | Skip = "Windows Only Test";
12 | }
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/test/Condenser.Tests.Integration/ListCallbackFacts.cs:
--------------------------------------------------------------------------------
1 | using CondenserDotNet.Client;
2 | using CondenserDotNet.Client.Services;
3 | using Microsoft.Extensions.Options;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Text;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 | using Xunit;
10 |
11 | namespace Condenser.Tests.Integration
12 | {
13 | public class ListCallbackFacts
14 | {
15 | [Fact]
16 | public async Task TestCallbackIsCalled()
17 | {
18 | var serviceName = Guid.NewGuid().ToString();
19 | var serviceCount = 100;
20 | var opts = Options.Create(new ServiceManagerConfig() { ServiceName = serviceName, ServicePort = 2222 });
21 | using (var manager = new ServiceManager(opts))
22 | using (var register = new ServiceRegistry())
23 | {
24 | register.SetServiceListCallback(serviceName, list =>
25 | {
26 | Volatile.Write(ref serviceCount, list?.Count ?? 0);
27 | });
28 | await Task.Delay(500);
29 | Assert.Equal(0, serviceCount);
30 |
31 | manager.AddTtlHealthCheck(10);
32 | var registerResult = await manager.RegisterServiceAsync();
33 | var ttlResult = await manager.TtlCheck.ReportPassingAsync();
34 |
35 | await Task.Delay(500);
36 | Assert.Equal(1, serviceCount);
37 |
38 | ttlResult = await manager.TtlCheck.ReportFailAsync();
39 |
40 | await Task.Delay(500);
41 | Assert.Equal(0, serviceCount);
42 | }
43 | }
44 |
45 |
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/test/Condenser.Tests.Integration/MaintenanceFacts.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Threading.Tasks;
5 | using CondenserDotNet.Client;
6 | using CondenserDotNet.Client.Services;
7 | using Microsoft.Extensions.Options;
8 | using Xunit;
9 |
10 | namespace Condenser.Tests.Integration
11 | {
12 | public class MaintenanceFacts
13 | {
14 | [Fact]
15 | public async Task TestIntoAndOutOfMaintenance()
16 | {
17 | var config = Options.Create(new ServiceManagerConfig());
18 | config.Value.ServiceAddress = "127.0.0.1";
19 | config.Value.ServicePort = 777;
20 | config.Value.ServiceName = Guid.NewGuid().ToString();
21 | var manager = new ServiceManager(config);
22 | await manager.RegisterServiceAsync();
23 | var registry = new ServiceRegistry();
24 | var instance = await registry.GetServiceInstanceAsync(config.Value.ServiceName);
25 | Assert.NotNull(instance);
26 |
27 | await manager.EnableMaintenanceModeAsync("down");
28 | await Task.Delay(200);
29 |
30 | instance = await registry.GetServiceInstanceAsync(config.Value.ServiceName);
31 | Assert.Null(instance);
32 |
33 | await manager.DisableMaintenanceModeAsync();
34 | await Task.Delay(200);
35 | instance = await registry.GetServiceInstanceAsync(config.Value.ServiceName);
36 | Assert.NotNull(instance);
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/test/Condenser.Tests.Integration/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyConfiguration("")]
9 | [assembly: AssemblyCompany("")]
10 | [assembly: AssemblyProduct("Condenser.Tests.Integration")]
11 | [assembly: AssemblyTrademark("")]
12 |
13 | // Setting ComVisible to false makes the types in this assembly not visible
14 | // to COM components. If you need to access a type in this assembly from
15 | // COM, set the ComVisible attribute to true on that type.
16 | [assembly: ComVisible(false)]
17 |
18 | // The following GUID is for the ID of the typelib if this project is exposed to COM
19 | [assembly: Guid("a7e55927-04d3-4edb-b598-6754611de8c6")]
20 |
--------------------------------------------------------------------------------
/test/Condenser.Tests.Integration/ProtocolSwitcherFacts.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Hosting;
2 | using Microsoft.DotNet.PlatformAbstractions;
3 | using System.IO;
4 | using System.Security.Cryptography.X509Certificates;
5 | using System.Threading.Tasks;
6 | using Xunit;
7 | using CondenserDotNet.Middleware.ProtocolSwitcher;
8 | using Microsoft.AspNetCore.Builder;
9 | using Microsoft.AspNetCore.Http;
10 | using System.Net.Http;
11 |
12 | namespace Condenser.Tests.Integration
13 | {
14 | public class ProtocolSwitcherFacts
15 | {
16 | public static X509Certificate2 Certificate = new X509Certificate2(Path.Combine(ApplicationEnvironment.ApplicationBasePath, @"TestCert.pfx"), "Test123t");
17 |
18 | [Fact]
19 | public async Task SwitcherooSeesHttpsFact()
20 | {
21 | var port = CondenserDotNet.Client.ServiceManagerConfig.GetNextAvailablePort();
22 | var host = new WebHostBuilder()
23 | .UseKestrel((ops) =>
24 | {
25 | ops.Switcheroo();
26 | ops.UseHttps(Certificate);
27 | })
28 | .UseUrls($"*://*:{port}")
29 | .UseStartup()
30 | .Build();
31 | host.Start();
32 |
33 | try
34 | {
35 | var client = new HttpClient(new HttpClientHandler()
36 | {
37 | ServerCertificateCustomValidationCallback = (request, cert, chain, policy) =>
38 | {
39 | return true;
40 | }
41 | });
42 |
43 | var result = await client.GetAsync($"https://localhost:{port}");
44 | var isHttp = await result.Content.ReadAsStringAsync();
45 | Assert.True(bool.Parse(isHttp));
46 | }
47 | finally
48 | {
49 | host.Dispose();
50 | }
51 | }
52 |
53 | [Fact]
54 | public async Task SwitcherooSeesHttpFact()
55 | {
56 | var port = CondenserDotNet.Client.ServiceManagerConfig.GetNextAvailablePort();
57 | var host = new WebHostBuilder()
58 | .UseKestrel((ops) =>
59 | {
60 | ops.Switcheroo();
61 | ops.UseHttps(Certificate);
62 | })
63 | .UseUrls($"*://*:{port}")
64 | .UseStartup()
65 | .Build();
66 | host.Start();
67 |
68 | try
69 | {
70 | var client = new HttpClient(new HttpClientHandler()
71 | {
72 | ServerCertificateCustomValidationCallback = (request, cert, chain, policy) =>
73 | {
74 | return false;
75 | }
76 | });
77 |
78 | var result = await client.GetAsync($"http://localhost:{port}");
79 | var isHttp = await result.Content.ReadAsStringAsync();
80 | Assert.False(bool.Parse(isHttp));
81 | }
82 | finally
83 | {
84 | host.Dispose();
85 | }
86 | }
87 |
88 | public class Startup
89 | {
90 | public void Configure(IApplicationBuilder app) =>
91 | app.Use(async (context, next) =>
92 | {
93 | await context.Response.WriteAsync(context.Request.IsHttps.ToString());
94 | return;
95 | });
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/test/Condenser.Tests.Integration/ServiceDeregistrationFacts.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Threading.Tasks;
5 | using CondenserDotNet.Client;
6 | using CondenserDotNet.Client.Services;
7 | using Microsoft.Extensions.Options;
8 | using Xunit;
9 |
10 | namespace Condenser.Tests.Integration
11 | {
12 | public class ServiceDeregistrationFacts
13 | {
14 | [Fact]
15 | public async Task CheckDeregistrationWorks()
16 | {
17 | var serviceName = Guid.NewGuid().ToString();
18 | var opts = Options.Create(new ServiceManagerConfig() { ServicePort = 2222, ServiceName = serviceName });
19 | using (var manager = new ServiceManager(opts))
20 | using (var registry = new ServiceRegistry())
21 | {
22 | var registrationResult = await manager.RegisterServiceAsync();
23 | Assert.True(registrationResult);
24 |
25 | var services = await registry.GetAvailableServicesAsync();
26 | Assert.Contains(serviceName, services);
27 |
28 | registrationResult = await manager.DeregisterServiceAsync();
29 | Assert.True(registrationResult);
30 |
31 | services = await registry.GetAvailableServicesAsync();
32 | Assert.DoesNotContain(serviceName, services);
33 | }
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/test/Condenser.Tests.Integration/ServiceRegistrationFacts.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using CondenserDotNet.Client;
4 | using CondenserDotNet.Client.Services;
5 | using Microsoft.Extensions.Options;
6 | using Xunit;
7 |
8 | namespace Condenser.Tests.Integration
9 | {
10 | public class ServiceRegistrationFacts
11 | {
12 | [Fact]
13 | public async Task TestRegister()
14 | {
15 | var serviceName = Guid.NewGuid().ToString();
16 | var opts = Options.Create(new ServiceManagerConfig() { ServicePort = 2222 });
17 | using (var manager = new ServiceManager(opts))
18 | {
19 | manager.AddApiUrl("api/testurl");
20 | var registrationResult = await manager.RegisterServiceAsync();
21 |
22 | Assert.True(registrationResult);
23 | }
24 | }
25 |
26 | [Fact]
27 | public async Task TestRegisterWithCustomTags()
28 | {
29 | var serviceName = Guid.NewGuid().ToString();
30 | var opts = Options.Create(new ServiceManagerConfig() { ServicePort = 2222, ServiceName = serviceName });
31 | using (var manager = new ServiceManager(opts))
32 | {
33 | manager.CustomTags.Add("CustomTag1");
34 | manager.CustomTags.Add("CustomTag2");
35 | manager.AddTtlHealthCheck(10);
36 | var registerResult = await manager.RegisterServiceAsync();
37 | var ttlResult = await manager.TtlCheck.ReportPassingAsync();
38 | using (var serviceRegistry = new ServiceRegistry())
39 | {
40 | var instance = await serviceRegistry.GetServiceInstanceAsync(serviceName);
41 | Assert.Contains("CustomTag1", instance.Tags);
42 | Assert.Contains("CustomTag2", instance.Tags);
43 | }
44 | }
45 | }
46 |
47 | [Fact]
48 | public async Task TestRegisterAndSetPassTtl()
49 | {
50 | var serviceName = Guid.NewGuid().ToString();
51 | var opts = Options.Create(new ServiceManagerConfig() { ServicePort = 2222 });
52 | using (var manager = new ServiceManager(opts))
53 | {
54 | manager.AddTtlHealthCheck(10);
55 | var registerResult = await manager.RegisterServiceAsync();
56 | var ttlResult = await manager.TtlCheck.ReportPassingAsync();
57 |
58 | Assert.True(ttlResult);
59 | }
60 | }
61 |
62 | [Fact]
63 | public async Task TestRegisterAndCheckRegistered()
64 | {
65 | var serviceName = Guid.NewGuid().ToString();
66 | var opts = Options.Create(new ServiceManagerConfig() { ServicePort = 2222, ServiceName = serviceName });
67 | using (var manager = new ServiceManager(opts))
68 | using (var registry = new ServiceRegistry())
69 | {
70 | var registrationResult = await manager.RegisterServiceAsync();
71 | Assert.True(registrationResult);
72 |
73 | var services = await registry.GetAvailableServicesAsync();
74 | Assert.Contains(serviceName, services);
75 | }
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/test/Condenser.Tests.Integration/TestCert.pfx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Drawaes/CondenserDotNet/0d1ca5de6900fbf7f9b2120724f92d05a79c2a6f/test/Condenser.Tests.Integration/TestCert.pfx
--------------------------------------------------------------------------------
/test/Condenser.Tests.Integration/TrailingHeaderMiddlewareFacts.cs:
--------------------------------------------------------------------------------
1 | using CondenserDotNet.Client;
2 | using CondenserDotNet.Middleware.TrailingHeaders;
3 | using Microsoft.AspNetCore.Builder;
4 | using Microsoft.AspNetCore.Hosting;
5 | using Microsoft.AspNetCore.Http;
6 | using System.Net.Http;
7 | using System.Threading.Tasks;
8 | using Xunit;
9 | using System.Linq;
10 | using Condenser.Tests.Integration.Internal;
11 |
12 | namespace Condenser.Tests.Integration
13 | {
14 | public class TrailingHeaderMiddlewareFacts
15 | {
16 | [WindowsOnlyFact]
17 | public async Task HeaderIsAdded()
18 | {
19 | var port = ServiceManagerConfig.GetNextAvailablePort();
20 |
21 | var host = new WebHostBuilder()
22 | .UseKestrel()
23 | .UseUrls($"http://*:{port}")
24 | .UseStartup()
25 | .Build();
26 | host.Start();
27 |
28 | try
29 | {
30 | var client = new HttpClient();
31 | var result = await client.GetAsync($"http://localhost:{port}");
32 | var content = await result.Content.ReadAsStringAsync();
33 | Assert.Equal("Hello", content);
34 | Assert.Equal("FunnyHeader", result.Headers.Trailer.First());
35 | }
36 | finally
37 | {
38 | host.Dispose();
39 | }
40 | }
41 |
42 | public class Startup
43 | {
44 | public void Configure(IApplicationBuilder app)
45 | {
46 | app.UseMiddleware();
47 | app.Use(async (context, next) =>
48 | {
49 | var trailingHeader = context.Features.Get();
50 | var headerValue = "";
51 | trailingHeader.RegisterHeader("FunnyHeader", () => headerValue);
52 |
53 | await context.Response.WriteAsync("Hello");
54 |
55 | headerValue = "DownUnder";
56 | return;
57 | });
58 | }
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/test/Condenser.Tests.Integration/xunit.runner.json:
--------------------------------------------------------------------------------
1 | {
2 | "methodDisplay": "method",
3 | "longRunningTestSeconds": 15
4 | }
--------------------------------------------------------------------------------
/test/CondenserTests/CondenserTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | CondenserTests
6 | CondenserTests
7 | true
8 | false
9 | false
10 | false
11 | latest
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | PreserveNewest
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/test/CondenserTests/ConsulConfigurationProviderTests.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using CondenserDotNet.Configuration;
3 | using CondenserTests.Fakes;
4 | using Microsoft.Extensions.Configuration;
5 | using Xunit;
6 |
7 | namespace CondenserTests
8 | {
9 | public class ConsulConfigurationProviderTests
10 | {
11 | [Fact]
12 | public void CanBindConfiguratonSection()
13 | {
14 | var registry = new FakeConfigurationRegistry();
15 | registry.SetKeyAsync("FakeConfig:Setting1", "abc");
16 | registry.SetKeyAsync("FakeConfig:Setting2", "def");
17 |
18 | var builder = new ConfigurationBuilder()
19 | .AddConfigurationRegistry(registry)
20 | .Build();
21 |
22 | var config = new FakeConfig();
23 | builder.GetSection("FakeConfig").Bind(config);
24 |
25 | Assert.Equal("abc", config.Setting1);
26 | Assert.Equal("def", config.Setting2);
27 | }
28 |
29 | [Fact]
30 | public void CanBindConfiguratonSectionAsList()
31 | {
32 | var registry = new FakeConfigurationRegistry();
33 | registry.SetKeyAsync("my/config/section/objectlist:0:Setting1", "1");
34 | registry.SetKeyAsync("my/config/section/objectlist:0:Setting2", "2");
35 | registry.SetKeyAsync("my/config/section/objectlist:1:Setting1", "3");
36 | registry.SetKeyAsync("my/config/section/objectlist:1:Setting2", "4");
37 |
38 | var builder = new ConfigurationBuilder()
39 | .AddConfigurationRegistry(registry)
40 | .Build();
41 |
42 | var config = new List();
43 | builder.GetSection("my/config/section/objectlist").Bind(config);
44 |
45 | Assert.Equal(2, config.Count);
46 | Assert.Equal("1", config[0].Setting1);
47 | Assert.Equal("2", config[0].Setting2);
48 | Assert.Equal("3", config[1].Setting1);
49 | Assert.Equal("4", config[1].Setting2);
50 | }
51 |
52 | [Fact]
53 | public void CanGetConfig()
54 | {
55 | const string key = "key1";
56 | const string keyValue = "value1";
57 |
58 | var registry = new FakeConfigurationRegistry();
59 | var sut = new ConfigurationRegistryProvider(registry);
60 |
61 | registry.SetKeyAsync(key, keyValue);
62 |
63 | sut.TryGet(key, out var value);
64 | Assert.Equal(keyValue, value);
65 | }
66 |
67 |
68 | [Fact]
69 | public void CanReloadConfig()
70 | {
71 | var reloaded = false;
72 |
73 | var registry = new FakeConfigurationRegistry();
74 | var sut = new ConfigurationRegistryProvider(registry);
75 |
76 | sut.Load();
77 | sut.GetReloadToken().RegisterChangeCallback(_ => reloaded = true, null);
78 |
79 | registry.FakeReload();
80 |
81 | Assert.True(reloaded);
82 | }
83 |
84 | [Fact]
85 | public void CanSetConfig()
86 | {
87 | const string key = "key1";
88 | const string keyValue = "value1";
89 |
90 | var registry = new FakeConfigurationRegistry();
91 | var sut = new ConfigurationRegistryProvider(registry);
92 |
93 | sut.Set(key, keyValue);
94 |
95 | sut.TryGet(key, out var value);
96 | Assert.Equal(keyValue, value);
97 | }
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/test/CondenserTests/Fakes/FakeConfig.cs:
--------------------------------------------------------------------------------
1 | namespace CondenserTests.Fakes
2 | {
3 | public class FakeConfig
4 | {
5 | public string Setting1 { get; set; }
6 | public string Setting2 { get; set; }
7 | }
8 | }
--------------------------------------------------------------------------------
/test/CondenserTests/Fakes/FakeConfigurationRegistry.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using CondenserDotNet.Configuration;
5 | using Microsoft.Extensions.Configuration;
6 |
7 | namespace CondenserTests.Fakes
8 | {
9 | internal class FakeConfigurationRegistry : IConfigurationRegistry
10 | {
11 | private readonly Dictionary _data = new Dictionary();
12 | private readonly List _reloadActions = new List();
13 |
14 |
15 | public FakeConfigurationRegistry() => Root = new ConfigurationBuilder()
16 | .AddConfigurationRegistry(this)
17 | .Build();
18 |
19 | public string this[string key] => _data[key];
20 |
21 | public Task AddStaticKeyPathAsync(string keyPath) => throw new NotImplementedException();
22 |
23 | public Task AddUpdatingPathAsync(string keyPath) => throw new NotImplementedException();
24 |
25 | public void AddWatchOnEntireConfig(Action callback) => _reloadActions.Add(callback);
26 |
27 | public void AddWatchOnSingleKey(string keyToWatch, Action callback) => throw new NotImplementedException();
28 |
29 | public Task SetKeyAsync(string keyPath, string value)
30 | {
31 | _data[keyPath] = value;
32 | return Task.FromResult(true);
33 | }
34 |
35 | public bool TryGetValue(string key, out string value) => _data.TryGetValue(key, out value);
36 | public IEnumerable AllKeys => _data.Keys;
37 |
38 | public IConfigurationRoot Root { get; }
39 |
40 | public void AddWatchOnSingleKey(string keyToWatch, Action callback) => throw new NotImplementedException();
41 |
42 |
43 | public void FakeReload()
44 | {
45 | foreach (var action in _reloadActions)
46 | action();
47 | }
48 |
49 | public void Dispose()
50 | {
51 |
52 | }
53 |
54 | public Task AddStaticKeyPathAsync(string keyPath, bool singleKey = false) => throw new NotImplementedException();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/test/CondenserTests/Fakes/FakeController.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc;
2 | using Microsoft.Extensions.Options;
3 |
4 | namespace CondenserTests.Fakes
5 | {
6 | [Route("[controller]")]
7 | public class FakeController : Controller
8 | {
9 | private readonly IOptions _config;
10 |
11 | public FakeController(IOptions config) => _config = config;
12 |
13 | public IActionResult Get() => Ok("Config: " + _config.Value.Setting1);
14 |
15 | [Route("fake/route")]
16 | public IActionResult GetFakeRoute() => Ok("Was routed");
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/test/CondenserTests/Fakes/FakeServiceManager.cs:
--------------------------------------------------------------------------------
1 | using CondenserDotNet.Client;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 | using CondenserDotNet.Client.DataContracts;
6 | using System.Net.Http;
7 | using System.Threading;
8 | using Microsoft.Extensions.Logging;
9 | using System.Threading.Tasks;
10 |
11 | namespace CondenserTests.Fakes
12 | {
13 | public class FakeServiceManager : IServiceManager
14 | {
15 | public string ServiceId { get; set; }
16 |
17 | public string ServiceName { get; set; }
18 |
19 | public TimeSpan DeregisterIfCriticalAfter { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
20 |
21 | public bool IsRegistered => throw new NotImplementedException();
22 |
23 | public ITtlCheck TtlCheck { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
24 |
25 | public string ServiceAddress { get; set; }
26 |
27 | public int ServicePort { get; set; }
28 |
29 | public CancellationToken Cancelled => throw new NotImplementedException();
30 |
31 | public ILogger Logger => null;
32 |
33 | public HttpClient Client => throw new NotImplementedException();
34 |
35 | public List SupportedUrls => throw new NotImplementedException();
36 |
37 | public HealthConfiguration HealthConfig { get; } = new HealthConfiguration();
38 |
39 | public Service RegisteredService { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
40 | public string ProtocolSchemeTag { get; set; }
41 |
42 | public List CustomTags => throw new NotImplementedException();
43 |
44 | public Task RegistrationTask => throw new NotImplementedException();
45 |
46 | public void Dispose() => throw new NotImplementedException();
47 |
48 | public bool UpdateRegistrationTask(Task inboundTask) => throw new NotImplementedException();
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/test/CondenserTests/GlobalSuppressions.cs:
--------------------------------------------------------------------------------
1 |
2 | // This file is used by Code Analysis to maintain SuppressMessage
3 | // attributes that are applied to this project.
4 | // Project-level suppressions either have no target or are given
5 | // a specific target and scoped to a namespace, type, member, etc.
6 |
7 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "xUnit1004:Test methods should not be skipped", Justification = "", Scope = "member", Target = "~M:CondenserTests.RadixTests.TestSplitting")]
8 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "xUnit1004:Test methods should not be skipped", Justification = "", Scope = "member", Target = "~M:CondenserTests.RadixTests.TestCompression")]
9 |
10 |
--------------------------------------------------------------------------------
/test/CondenserTests/HealthConfigurationFacts.cs:
--------------------------------------------------------------------------------
1 | using CondenserDotNet.Client;
2 | using CondenserTests.Fakes;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Text;
6 | using Xunit;
7 |
8 | namespace CondenserTests
9 | {
10 | public class HealthConfigurationFacts
11 | {
12 | [Theory]
13 | [InlineData("http://someotheraddress/health", true, "http://someotheraddress/health")]
14 | [InlineData("/health", true, "https://localhost:8000/health")]
15 | [InlineData("health", false, "https://localhost:8000/health")]
16 | public void ShouldBuildHttpsExpectedUrl(string url, bool ignoreForCheck, string expectedUrl)
17 | {
18 | var id = "myservice";
19 | var interval = 50;
20 |
21 | var manager = new FakeServiceManager
22 | {
23 | ServicePort = 8000,
24 | ServiceAddress = "localhost",
25 | ServiceId = id
26 | };
27 |
28 | manager.AddHttpHealthCheck(url, interval);
29 | manager.UseHttps(ignoreForCheck);
30 |
31 | var check = manager.HealthConfig.Build(manager);
32 |
33 | Assert.Equal(expectedUrl, check.HTTP);
34 | Assert.Equal(ignoreForCheck, check.tls_skip_verify);
35 | }
36 |
37 | [Theory]
38 | [InlineData("http://someotheraddress/health", "http://someotheraddress/health")]
39 | [InlineData("/health", "http://localhost:8000/health")]
40 | [InlineData("health", "http://localhost:8000/health")]
41 | public void ShouldBuildExpectedUrl(string url, string expectedUrl)
42 | {
43 | var id = "myservice";
44 | var interval = 50;
45 |
46 | var manager = new FakeServiceManager
47 | {
48 | ServicePort = 8000,
49 | ServiceAddress = "localhost",
50 | ServiceId = id
51 | };
52 |
53 | manager.AddHttpHealthCheck(url, interval);
54 |
55 | var check = manager.HealthConfig.Build(manager);
56 |
57 | Assert.Equal(expectedUrl, check.HTTP);
58 | Assert.Equal($"{interval}s", check.Interval);
59 | Assert.Equal($"{id}:HttpCheck", check.Name);
60 | }
61 |
62 | [Fact]
63 | public void ShouldBuildNoHealthCheckWhenUrlNotSet()
64 | {
65 | var id = "myservice";
66 |
67 | var manager = new FakeServiceManager
68 | {
69 | ServicePort = 8000,
70 | ServiceAddress = "localhost",
71 | ServiceId = id
72 | };
73 |
74 | var check = manager.HealthConfig.Build(manager);
75 | Assert.Null(check);
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/test/CondenserTests/JsonKeyValueParserTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using CondenserDotNet.Configuration.Consul;
6 | using CondenserTests.Fakes;
7 | using Newtonsoft.Json;
8 | using Xunit;
9 |
10 | namespace CondenserTests
11 | {
12 | public class JsonKeyValueParserTests
13 | {
14 | [Fact]
15 | public void CanParseList()
16 | {
17 | var fakeConfigs = new List
18 | {
19 | new FakeConfig
20 | {
21 | Setting1 = "one",
22 | Setting2 = "two"
23 | },
24 | new FakeConfig
25 | {
26 | Setting1 = "three",
27 | Setting2 = "four"
28 | }
29 | };
30 | var key = "my/config/section/objectlist";
31 | var json = JsonConvert.SerializeObject(fakeConfigs);
32 | var parser = new JsonKeyValueParser();
33 | var keyValue = new KeyValue
34 | {
35 | Key = key,
36 | Value = Convert.ToBase64String(Encoding.UTF8.GetBytes(json))
37 | };
38 |
39 | var keys = parser.Parse(keyValue)
40 | .ToArray();
41 |
42 | Assert.Equal(4, keys.Length);
43 | Assert.Equal("my/config/section/objectlist:0:Setting1", keys[0].Key);
44 | Assert.Equal("my/config/section/objectlist:0:Setting2", keys[1].Key);
45 | Assert.Equal("my/config/section/objectlist:1:Setting1", keys[2].Key);
46 | Assert.Equal("my/config/section/objectlist:1:Setting2", keys[3].Key);
47 | }
48 |
49 | [Fact]
50 | public void CanParseJsonObjectToRequireOptionsFormat()
51 | {
52 | var dto = JsonConvert.SerializeObject(new
53 | {
54 | Port = 1234,
55 | Server = "localhost"
56 | });
57 |
58 | var keyValue = new KeyValue
59 | {
60 | Key = "smtp",
61 | Value = Convert.ToBase64String(Encoding.UTF8.GetBytes(dto))
62 | };
63 |
64 | var data = new JsonKeyValueParser()
65 | .Parse(keyValue)
66 | .ToArray();
67 |
68 | Assert.Equal(2, data.Length);
69 | Assert.Equal("smtp:Port", data[0].Key);
70 | Assert.Equal("1234", data[0].Value);
71 | Assert.Equal("smtp:Server", data[1].Key);
72 | Assert.Equal("localhost", data[1].Value);
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/test/CondenserTests/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyConfiguration("")]
9 | [assembly: AssemblyCompany("")]
10 | [assembly: AssemblyProduct("CondenserTests")]
11 | [assembly: AssemblyTrademark("")]
12 |
13 | // Setting ComVisible to false makes the types in this assembly not visible
14 | // to COM components. If you need to access a type in this assembly from
15 | // COM, set the ComVisible attribute to true on that type.
16 | [assembly: ComVisible(false)]
17 |
18 | // The following GUID is for the ID of the typelib if this project is exposed to COM
19 | [assembly: Guid("17e329c7-cac6-4d6a-8d83-a4c68476dd04")]
20 |
--------------------------------------------------------------------------------
/test/CondenserTests/RountingStrategyFacts.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Xunit;
3 |
4 | namespace CondenserTests
5 | {
6 | public class RountingStrategyFacts
7 | {
8 | private readonly List _services = new List()
9 | {
10 | new RoutedService() { Id = 1 },
11 | new RoutedService() { Id = 2 },
12 | };
13 |
14 | [Fact]
15 | public void RoundRobinFact()
16 | {
17 | var router = new CondenserDotNet.Core.Routing.RoundRobinRoutingStrategy();
18 | var service1 = router.RouteTo(_services);
19 | var service2 = router.RouteTo(_services);
20 | var service3 = router.RouteTo(_services);
21 |
22 | Assert.Equal(1, service1.Id);
23 | Assert.Equal(2, service2.Id);
24 | Assert.Equal(1, service3.Id);
25 | }
26 |
27 | [Fact]
28 | public void RoundRobinEmptyListFact()
29 | {
30 | var router = new CondenserDotNet.Core.Routing.RoundRobinRoutingStrategy();
31 | var service = router.RouteTo(new List());
32 | Assert.Null(service);
33 | }
34 |
35 | [Fact]
36 | public void RoundRobinNullListFact()
37 | {
38 | var router = new CondenserDotNet.Core.Routing.RoundRobinRoutingStrategy();
39 | var service = router.RouteTo(null);
40 | Assert.Null(service);
41 | }
42 |
43 | private class RoutedService
44 | {
45 | public int Id { get; set; }
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/test/CondenserTests/ServiceManagerConfigurationFacts.cs:
--------------------------------------------------------------------------------
1 | using CondenserDotNet.Client;
2 | using Microsoft.Extensions.Options;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Text;
6 | using Xunit;
7 |
8 | namespace CondenserTests
9 | {
10 | public class ServiceManagerConfigurationFacts
11 | {
12 | [Fact]
13 | public void DefaultsOverrideCorrectlyTest()
14 | {
15 | var opts = Options.Create(new ServiceManagerConfig() { ServicePort = 2222, ServiceName = "Test1", ServiceId = "ServiceId" });
16 | using (var manager = new ServiceManager(opts))
17 | {
18 | Assert.Equal(2222, manager.ServicePort);
19 | Assert.Equal("Test1", manager.ServiceName);
20 | Assert.Equal("ServiceId", manager.ServiceId);
21 | }
22 | }
23 |
24 | [Fact]
25 | public void DefaultsCorrectly()
26 | {
27 | var opts = Options.Create(new ServiceManagerConfig() { ServicePort = 2222 });
28 | using (var manager = new ServiceManager(opts))
29 | {
30 | Assert.NotNull(manager.ServiceName);
31 | Assert.NotNull(manager.ServiceId);
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/test/CondenserTests/TagsToRoutesFacts.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | namespace CondenserTests
4 | {
5 | public class TagsToRoutesFacts
6 | {
7 | private const string UrlPrefix = "urlprefix-";
8 |
9 | [Fact]
10 | public void TagsWithoutRoute()
11 | {
12 | var routes = CondenserDotNet.Core.ServiceUtils.RoutesFromTags(new string[] { $"{UrlPrefix}/this/url/is/cool", "thisTagIsnt" });
13 | Assert.Single(routes);
14 | Assert.Equal("/this/url/is/cool", routes[0]);
15 | }
16 |
17 | [Fact]
18 | public void SlashAddedToTheFront()
19 | {
20 | var routes = CondenserDotNet.Core.ServiceUtils.RoutesFromTags(new string[] { $"{UrlPrefix}this/url/is/almost/cool" });
21 | Assert.Equal("/this/url/is/almost/cool", routes[0]);
22 | }
23 |
24 | [Fact]
25 | public void SlashRemovedFromTheBack()
26 | {
27 | var routes = CondenserDotNet.Core.ServiceUtils.RoutesFromTags(new string[] { $"{UrlPrefix}this/url/needs/work/" });
28 | Assert.Equal("/this/url/needs/work", routes[0]);
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/test/CondenserTests/TestServerFacts.cs:
--------------------------------------------------------------------------------
1 | using CondenserDotNet.Configuration;
2 | using CondenserTests.Fakes;
3 | using Microsoft.AspNetCore.Builder;
4 | using Microsoft.AspNetCore.Hosting;
5 | using Microsoft.AspNetCore.TestHost;
6 | using Microsoft.Extensions.Configuration;
7 | using Microsoft.Extensions.DependencyInjection;
8 | using Xunit;
9 |
10 | namespace CondenserTests
11 | {
12 | public class TestServerFacts
13 | {
14 | private const string UrlPrefix = "urlprefix-";
15 |
16 | [Fact]
17 | public async void CanReloadOptionDetails()
18 | {
19 | var registry = new FakeConfigurationRegistry();
20 | await registry.SetKeyAsync("FakeConfig:Setting1", "abc");
21 | await registry.SetKeyAsync("FakeConfig:Setting2", "def");
22 |
23 | var builder = new WebHostBuilder()
24 | .Configure(x => x.UseMvcWithDefaultRoute())
25 | .ConfigureServices(x =>
26 | {
27 | x.AddMvcCore();
28 | x.AddOptions();
29 | x.ConfigureReloadable(registry);
30 | });
31 |
32 | using (var server = new TestServer(builder))
33 | {
34 | using (var client = server.CreateClient())
35 | {
36 | var response = await client.GetAsync("Fake");
37 | var setting = await response.Content.ReadAsStringAsync();
38 |
39 | Assert.Equal("Config: abc", setting);
40 |
41 | await registry.SetKeyAsync("FakeConfig:Setting1", "this is the new setting");
42 | registry.FakeReload();
43 |
44 | response = await client.GetAsync("Fake");
45 | setting = await response.Content.ReadAsStringAsync();
46 |
47 | Assert.Equal("Config: this is the new setting", setting);
48 | }
49 | }
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/test/CondenserTests/xunit.runner.json:
--------------------------------------------------------------------------------
1 | {
2 | "methodDisplay": "method",
3 | "longRunningTestSeconds": 15
4 | }
--------------------------------------------------------------------------------
/version.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | 5.2.1
4 | beta
5 |
6 |
7 |
--------------------------------------------------------------------------------