├── .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 | ![Logo](https://drawaes.github.io/CondenserDocs/assets/img/logo.png) 2 | 3 | [![Build status](https://ci.appveyor.com/api/projects/status/r2088yqbhp57cu66?svg=true)](https://ci.appveyor.com/project/Drawaes/condenserdotnet) 4 | [![Xplat Build status](https://travis-ci.org/Drawaes/CondenserDotNet.svg?branch=master)](https://travis-ci.org/Drawaes/CondenserDotNet) 5 | [![Join the chat at https://gitter.im/CondenserDotNet/Lobby](https://badges.gitter.im/CondenserDotNet/Lobby.svg)](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 | [![MyGet](https://img.shields.io/myget/condenserdotnet/v/CondenserDotNet.Client.svg)](https://www.myget.org/F/condenserdotnet/api/v3/index.json) 15 | 16 | Current release is available at 17 | 18 | [![NuGet](https://img.shields.io/nuget/v/CondenserDotNet.Client.svg)](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 | --------------------------------------------------------------------------------