mirror of
				https://github.com/nsnail/Ocelot.git
				synced 2025-11-04 22:30:50 +08:00 
			
		
		
		
	Merge branch 'develop' of https://github.com/geffzhang/Ocelot into develop
This commit is contained in:
		
							
								
								
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -235,3 +235,10 @@ _Pvt_Extensions
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# FAKE - F# Make
 | 
					# FAKE - F# Make
 | 
				
			||||||
.fake/
 | 
					.fake/
 | 
				
			||||||
 | 
					tools/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# MacOS
 | 
				
			||||||
 | 
					.DS_Store
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Ocelot acceptance test config
 | 
				
			||||||
 | 
					test/Ocelot.AcceptanceTests/configuration.json
 | 
				
			||||||
							
								
								
									
										4
									
								
								GitVersion.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								GitVersion.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					mode: ContinuousDelivery
 | 
				
			||||||
 | 
					branches: {}
 | 
				
			||||||
 | 
					ignore:
 | 
				
			||||||
 | 
					  sha: []
 | 
				
			||||||
@@ -1,44 +0,0 @@
 | 
				
			|||||||
<?xml version="1.0"?>
 | 
					 | 
				
			||||||
<package >
 | 
					 | 
				
			||||||
  <metadata>
 | 
					 | 
				
			||||||
    <id>Ocelot</id>
 | 
					 | 
				
			||||||
    <version>1.0.0</version>
 | 
					 | 
				
			||||||
    <authors>Tom Pallister</authors>
 | 
					 | 
				
			||||||
    <owners>Tom Pallister</owners>
 | 
					 | 
				
			||||||
    <licenseUrl>https://github.com/TomPallister/Ocelot/blob/develop/LICENSE.md</licenseUrl>
 | 
					 | 
				
			||||||
    <projectUrl>https://github.com/TomPallister/Ocelot</projectUrl>
 | 
					 | 
				
			||||||
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
 | 
					 | 
				
			||||||
    <description>Ocelot Api Gateway</description>
 | 
					 | 
				
			||||||
    <releaseNotes>Latest Ocelot</releaseNotes>
 | 
					 | 
				
			||||||
    <copyright></copyright>
 | 
					 | 
				
			||||||
    <tags></tags>
 | 
					 | 
				
			||||||
    <dependencies>
 | 
					 | 
				
			||||||
		<dependency id="Microsoft.AspNetCore.Mvc" version="1.0.0" />
 | 
					 | 
				
			||||||
        <dependency id="Microsoft.AspNetCore.Server.IISIntegration" version="1.0.0" />
 | 
					 | 
				
			||||||
        <dependency id="Microsoft.AspNetCore.Server.Kestrel" version="1.0.0" />
 | 
					 | 
				
			||||||
        <dependency id="Microsoft.Extensions.Configuration.EnvironmentVariables" version="1.0.0" />
 | 
					 | 
				
			||||||
        <dependency id="Microsoft.Extensions.Configuration.FileExtensions" version="1.0.0" />
 | 
					 | 
				
			||||||
        <dependency id="Microsoft.Extensions.Configuration.Json" version="1.0.0" />
 | 
					 | 
				
			||||||
        <dependency id="Microsoft.Extensions.Logging" version="1.0.0" />
 | 
					 | 
				
			||||||
        <dependency id="Microsoft.Extensions.Logging.Console" version="1.0.0" />
 | 
					 | 
				
			||||||
        <dependency id="Microsoft.Extensions.Logging.Debug" version="1.0.0" />
 | 
					 | 
				
			||||||
        <dependency id="Microsoft.Extensions.Options.ConfigurationExtensions" version="1.0.0" />
 | 
					 | 
				
			||||||
        <dependency id="Microsoft.AspNetCore.Http" version="1.0.0" />
 | 
					 | 
				
			||||||
        <dependency id="System.Text.RegularExpressions" version="4.1.0" />
 | 
					 | 
				
			||||||
        <dependency id="Microsoft.AspNetCore.Authentication.OAuth" version="1.0.0" />
 | 
					 | 
				
			||||||
        <dependency id="Microsoft.AspNetCore.Authentication.JwtBearer" version="1.0.0" /> 
 | 
					 | 
				
			||||||
        <dependency id="Microsoft.AspNetCore.Authentication.OpenIdConnect" version="1.0.0" />
 | 
					 | 
				
			||||||
        <dependency id="Microsoft.AspNetCore.Authentication.Cookies" version="1.0.0" />
 | 
					 | 
				
			||||||
        <dependency id="Microsoft.AspNetCore.Authentication.Google" version="1.0.0" />
 | 
					 | 
				
			||||||
        <dependency id="Microsoft.AspNetCore.Authentication.Facebook" version="1.0.0" />
 | 
					 | 
				
			||||||
        <dependency id="Microsoft.AspNetCore.Authentication.Twitter" version="1.0.0" />
 | 
					 | 
				
			||||||
        <dependency id="Microsoft.AspNetCore.Authentication.MicrosoftAccount" version="1.0.0" />
 | 
					 | 
				
			||||||
        <dependency id="Microsoft.AspNetCore.Authentication" version="1.0.0" />
 | 
					 | 
				
			||||||
        <dependency id="IdentityServer4.AccessTokenValidation" version="1.0.1-rc2" />
 | 
					 | 
				
			||||||
    </dependencies>
 | 
					 | 
				
			||||||
  </metadata>
 | 
					 | 
				
			||||||
  <files>
 | 
					 | 
				
			||||||
        <file src="src\Ocelot\bin\Release\netcoreapp1.4\Ocelot.dll" target="lib\netstandard1.4" />
 | 
					 | 
				
			||||||
        <file src="src\Ocelot\bin\Release\netcoreapp1.4\Ocelot.pdb" target="lib\netstandard1.4" />
 | 
					 | 
				
			||||||
  </files>
 | 
					 | 
				
			||||||
</package>
 | 
					 | 
				
			||||||
							
								
								
									
										28
									
								
								Ocelot.sln
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								Ocelot.sln
									
									
									
									
									
								
							@@ -8,17 +8,24 @@ EndProject
 | 
				
			|||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3FA7C349-DBE8-4904-A2CE-015B8869CE6C}"
 | 
					Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3FA7C349-DBE8-4904-A2CE-015B8869CE6C}"
 | 
				
			||||||
	ProjectSection(SolutionItems) = preProject
 | 
						ProjectSection(SolutionItems) = preProject
 | 
				
			||||||
		.gitignore = .gitignore
 | 
							.gitignore = .gitignore
 | 
				
			||||||
		appveyor.yml = appveyor.yml
 | 
							build-and-release-unstable.ps1 = build-and-release-unstable.ps1
 | 
				
			||||||
		build-and-run-tests.bat = build-and-run-tests.bat
 | 
							build-and-run-tests.ps1 = build-and-run-tests.ps1
 | 
				
			||||||
		build.bat = build.bat
 | 
							build.cake = build.cake
 | 
				
			||||||
 | 
							build.ps1 = build.ps1
 | 
				
			||||||
 | 
							build.readme.md = build.readme.md
 | 
				
			||||||
		configuration-explanation.txt = configuration-explanation.txt
 | 
							configuration-explanation.txt = configuration-explanation.txt
 | 
				
			||||||
 | 
							configuration.yaml = configuration.yaml
 | 
				
			||||||
 | 
							GitVersion.yml = GitVersion.yml
 | 
				
			||||||
		global.json = global.json
 | 
							global.json = global.json
 | 
				
			||||||
		LICENSE.md = LICENSE.md
 | 
							LICENSE.md = LICENSE.md
 | 
				
			||||||
		Ocelot.nuspec = Ocelot.nuspec
 | 
							ocelot.postman_collection.json = ocelot.postman_collection.json
 | 
				
			||||||
		push-to-nuget.bat = push-to-nuget.bat
 | 
					 | 
				
			||||||
		README.md = README.md
 | 
							README.md = README.md
 | 
				
			||||||
		run-benchmarks.bat = run-benchmarks.bat
 | 
							release.ps1 = release.ps1
 | 
				
			||||||
		run-tests.bat = run-tests.bat
 | 
							ReleaseNotes.md = ReleaseNotes.md
 | 
				
			||||||
 | 
							run-acceptance-tests.ps1 = run-acceptance-tests.ps1
 | 
				
			||||||
 | 
							run-benchmarks.ps1 = run-benchmarks.ps1
 | 
				
			||||||
 | 
							run-unit-tests.ps1 = run-unit-tests.ps1
 | 
				
			||||||
 | 
							version.ps1 = version.ps1
 | 
				
			||||||
	EndProjectSection
 | 
						EndProjectSection
 | 
				
			||||||
EndProject
 | 
					EndProject
 | 
				
			||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Ocelot", "src\Ocelot\Ocelot.xproj", "{D6DF4206-0DBA-41D8-884D-C3E08290FDBB}"
 | 
					Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Ocelot", "src\Ocelot\Ocelot.xproj", "{D6DF4206-0DBA-41D8-884D-C3E08290FDBB}"
 | 
				
			||||||
@@ -33,6 +40,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Ocelot.ManualTest", "test\O
 | 
				
			|||||||
EndProject
 | 
					EndProject
 | 
				
			||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Ocelot.Benchmarks", "test\Ocelot.Benchmarks\Ocelot.Benchmarks.xproj", "{106B49E6-95F6-4A7B-B81C-96BFA74AF035}"
 | 
					Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Ocelot.Benchmarks", "test\Ocelot.Benchmarks\Ocelot.Benchmarks.xproj", "{106B49E6-95F6-4A7B-B81C-96BFA74AF035}"
 | 
				
			||||||
EndProject
 | 
					EndProject
 | 
				
			||||||
 | 
					Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Ocelot.IntegrationTests", "test\Ocelot.IntegrationTests\Ocelot.IntegrationTests.xproj", "{D4575572-99CA-4530-8737-C296EDA326F8}"
 | 
				
			||||||
 | 
					EndProject
 | 
				
			||||||
Global
 | 
					Global
 | 
				
			||||||
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 | 
						GlobalSection(SolutionConfigurationPlatforms) = preSolution
 | 
				
			||||||
		Debug|Any CPU = Debug|Any CPU
 | 
							Debug|Any CPU = Debug|Any CPU
 | 
				
			||||||
@@ -59,6 +68,10 @@ Global
 | 
				
			|||||||
		{106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
							{106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
				
			||||||
		{106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
							{106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
				
			||||||
		{106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
							{106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
				
			||||||
 | 
							{D4575572-99CA-4530-8737-C296EDA326F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
				
			||||||
 | 
							{D4575572-99CA-4530-8737-C296EDA326F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
				
			||||||
 | 
							{D4575572-99CA-4530-8737-C296EDA326F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
				
			||||||
 | 
							{D4575572-99CA-4530-8737-C296EDA326F8}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
				
			||||||
	EndGlobalSection
 | 
						EndGlobalSection
 | 
				
			||||||
	GlobalSection(SolutionProperties) = preSolution
 | 
						GlobalSection(SolutionProperties) = preSolution
 | 
				
			||||||
		HideSolutionNode = FALSE
 | 
							HideSolutionNode = FALSE
 | 
				
			||||||
@@ -69,5 +82,6 @@ Global
 | 
				
			|||||||
		{F8C224FE-36BE-45F5-9B0E-666D8F4A9B52} = {5B401523-36DA-4491-B73A-7590A26E420B}
 | 
							{F8C224FE-36BE-45F5-9B0E-666D8F4A9B52} = {5B401523-36DA-4491-B73A-7590A26E420B}
 | 
				
			||||||
		{02BBF4C5-517E-4157-8D21-4B8B9E118B7A} = {5B401523-36DA-4491-B73A-7590A26E420B}
 | 
							{02BBF4C5-517E-4157-8D21-4B8B9E118B7A} = {5B401523-36DA-4491-B73A-7590A26E420B}
 | 
				
			||||||
		{106B49E6-95F6-4A7B-B81C-96BFA74AF035} = {5B401523-36DA-4491-B73A-7590A26E420B}
 | 
							{106B49E6-95F6-4A7B-B81C-96BFA74AF035} = {5B401523-36DA-4491-B73A-7590A26E420B}
 | 
				
			||||||
 | 
							{D4575572-99CA-4530-8737-C296EDA326F8} = {5B401523-36DA-4491-B73A-7590A26E420B}
 | 
				
			||||||
	EndGlobalSection
 | 
						EndGlobalSection
 | 
				
			||||||
EndGlobal
 | 
					EndGlobal
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										216
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										216
									
								
								README.md
									
									
									
									
									
								
							@@ -1,6 +1,6 @@
 | 
				
			|||||||
# Ocelot
 | 
					# Ocelot
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[](https://ci.appveyor.com/project/TomPallister/ocelot)
 | 
					[](https://ci.appveyor.com/project/TomPallister/ocelot-fcfpb)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[](https://gitter.im/Ocelotey/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 | 
					[](https://gitter.im/Ocelotey/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -30,9 +30,6 @@ and retrived as the requests goes back up the Ocelot pipeline. There is a piece
 | 
				
			|||||||
that maps the HttpResponseMessage onto the HttpResponse object and that is returned to the client.
 | 
					that maps the HttpResponseMessage onto the HttpResponse object and that is returned to the client.
 | 
				
			||||||
That is basically it with a bunch of other features.
 | 
					That is basically it with a bunch of other features.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
This is not ready for production yet as uses a lot of rc and beta .net core packages.
 | 
					 | 
				
			||||||
Hopefully by the start of 2017 it will be in use.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## Contributing
 | 
					## Contributing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Pull requests, issues and commentary welcome! No special process just create a request and get in 
 | 
					Pull requests, issues and commentary welcome! No special process just create a request and get in 
 | 
				
			||||||
@@ -41,13 +38,13 @@ touch either via gitter or create an issue.
 | 
				
			|||||||
## How to install
 | 
					## How to install
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Ocelot is designed to work with ASP.NET core only and is currently 
 | 
					Ocelot is designed to work with ASP.NET core only and is currently 
 | 
				
			||||||
built to netcoreapp1.4 [this](https://docs.microsoft.com/en-us/dotnet/articles/standard/library) documentation may prove helpful when working out if Ocelot would be suitable for you.
 | 
					built to netcoreapp1.1 [this](https://docs.microsoft.com/en-us/dotnet/articles/standard/library) documentation may prove helpful when working out if Ocelot would be suitable for you.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Install Ocelot and it's dependecies using nuget. At the moment 
 | 
					Install Ocelot and it's dependecies using nuget. At the moment 
 | 
				
			||||||
all we have is the pre version. Once we have something working in 
 | 
					all we have is the pre version. Once we have something working in 
 | 
				
			||||||
a half decent way we will drop a version.
 | 
					a half decent way we will drop a version.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
`Install-Package Ocelot -Pre`
 | 
					`Install-Package Ocelot`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
All versions can be found [here](https://www.nuget.org/packages/Ocelot/)
 | 
					All versions can be found [here](https://www.nuget.org/packages/Ocelot/)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -61,11 +58,12 @@ The ReRoutes are the objects that tell Ocelot how to treat an upstream request.
 | 
				
			|||||||
configuration is a bit hacky and allows overrides of ReRoute specific settings. It's useful
 | 
					configuration is a bit hacky and allows overrides of ReRoute specific settings. It's useful
 | 
				
			||||||
if you don't want to manage lots of ReRoute specific settings.
 | 
					if you don't want to manage lots of ReRoute specific settings.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    "ReRoutes": [],
 | 
					    "ReRoutes": [],
 | 
				
			||||||
    "GlobalConfiguration": {}
 | 
					    "GlobalConfiguration": {}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
More information on how to use these options is below..
 | 
					More information on how to use these options is below..
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Startup
 | 
					## Startup
 | 
				
			||||||
@@ -73,6 +71,7 @@ More information on how to use these options is below..
 | 
				
			|||||||
An example startup using a json file for configuration can be seen below. 
 | 
					An example startup using a json file for configuration can be seen below. 
 | 
				
			||||||
Currently this is the only way to get configuration into Ocelot.
 | 
					Currently this is the only way to get configuration into Ocelot.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```csharp
 | 
				
			||||||
public class Startup
 | 
					public class Startup
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public Startup(IHostingEnvironment env)
 | 
					    public Startup(IHostingEnvironment env)
 | 
				
			||||||
@@ -101,18 +100,46 @@ Currently this is the only way to get configuration into Ocelot.
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            services.AddOcelotOutputCaching(settings);
 | 
					            services.AddOcelotOutputCaching(settings);
 | 
				
			||||||
            services.AddOcelotFileConfiguration(Configuration);
 | 
					            services.AddOcelot(Configuration);
 | 
				
			||||||
            services.AddOcelot();
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
 | 
					        public async void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
 | 
					            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            app.UseOcelot();
 | 
					            await app.UseOcelot();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Then in your Program.cs you will want to have the following. This can be changed if you 
 | 
				
			||||||
 | 
					don't wan't to use the default url e.g. UseUrls(someUrls) and should work as long as you keep the WebHostBuilder registration.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```csharp
 | 
				
			||||||
 | 
					 public class Program
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public static void Main(string[] args)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            IWebHostBuilder builder = new WebHostBuilder();
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            builder.ConfigureServices(s => {
 | 
				
			||||||
 | 
					                s.AddSingleton(builder);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            builder.UseKestrel()
 | 
				
			||||||
 | 
					                .UseContentRoot(Directory.GetCurrentDirectory())
 | 
				
			||||||
 | 
					                .UseStartup<Startup>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var host = builder.Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            host.Run();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Sadly we need to inject the IWebHostBuilder interface to get the applications scheme, url and port later. I cannot 
 | 
				
			||||||
 | 
					find a better way of doing this at the moment without setting this in a static or some kind of config.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
This is pretty much all you need to get going.......more to come! 
 | 
					This is pretty much all you need to get going.......more to come! 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -122,49 +149,140 @@ Ocelot's primary functionality is to take incomeing http requests and forward th
 | 
				
			|||||||
to a downstream service. At the moment in the form of another http request (in the future
 | 
					to a downstream service. At the moment in the form of another http request (in the future
 | 
				
			||||||
this could be any transport mechanism.). 
 | 
					this could be any transport mechanism.). 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Ocelot always adds a trailing slash to an UpstreamTemplate.
 | 
					Ocelot always adds a trailing slash to an UpstreamPathTemplate.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Ocelot's describes the routing of one request to another as a ReRoute. In order to get 
 | 
					Ocelot's describes the routing of one request to another as a ReRoute. In order to get 
 | 
				
			||||||
anything working in Ocelot you need to set up a ReRoute in the configuration.
 | 
					anything working in Ocelot you need to set up a ReRoute in the configuration.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    "ReRoutes": [
 | 
					    "ReRoutes": [
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
In order to set up a ReRoute you need to add one to the json array called ReRoutes like
 | 
					In order to set up a ReRoute you need to add one to the json array called ReRoutes like
 | 
				
			||||||
the following.
 | 
					the following.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
            "DownstreamTemplate": "http://jsonplaceholder.typicode.com/posts/{postId}",
 | 
					    "DownstreamPathTemplate": "/api/posts/{postId}",
 | 
				
			||||||
            "UpstreamTemplate": "/posts/{postId}",
 | 
					    "DownstreamScheme": "https",
 | 
				
			||||||
 | 
					    "DownstreamPort": 80,
 | 
				
			||||||
 | 
					    "DownstreamHost" "localhost"
 | 
				
			||||||
 | 
					    "UpstreamPathTemplate": "/posts/{postId}",
 | 
				
			||||||
    "UpstreamHttpMethod": "Put"
 | 
					    "UpstreamHttpMethod": "Put"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The DownstreamTemplate is the URL that this request will be forwarded to.
 | 
					The DownstreamPathTemplate,Scheme, Port and Host make the URL that this request will be forwarded to.
 | 
				
			||||||
The UpstreamTemplate is the URL that Ocelot will use to identity which 
 | 
					The UpstreamPathTemplate is the URL that Ocelot will use to identity which 
 | 
				
			||||||
DownstreamTemplate to use for a given request. Finally the UpstreamHttpMethod is used so
 | 
					DownstreamPathTemplate to use for a given request. Finally the UpstreamHttpMethod is used so
 | 
				
			||||||
Ocelot can distinguish between requests to the same URL and is obviously needed to work :)
 | 
					Ocelot can distinguish between requests to the same URL and is obviously needed to work :)
 | 
				
			||||||
In Ocelot you can add placeholders for variables to your Templates in the form of {something}.
 | 
					In Ocelot you can add placeholders for variables to your Templates in the form of {something}.
 | 
				
			||||||
The placeholder needs to be in both the DownstreamTemplate and UpstreamTemplate. If it is
 | 
					The placeholder needs to be in both the DownstreamPathTemplate and UpstreamPathTemplate. If it is
 | 
				
			||||||
Ocelot will attempt to replace the placeholder with the correct variable value from the 
 | 
					Ocelot will attempt to replace the placeholder with the correct variable value from the 
 | 
				
			||||||
Upstream URL when the request comes in.
 | 
					Upstream URL when the request comes in.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
At the moment without any configuration Ocelot will default to all ReRoutes being case insensitive.
 | 
					At the moment without any configuration Ocelot will default to all ReRoutes being case insensitive.
 | 
				
			||||||
In order to change this you can specify on a per ReRoute basis the following setting.
 | 
					In order to change this you can specify on a per ReRoute basis the following setting.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
"ReRouteIsCaseSensitive": true
 | 
					"ReRouteIsCaseSensitive": true
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
This means that when Ocelot tries to match the incoming upstream url with an upstream template the
 | 
					This means that when Ocelot tries to match the incoming upstream url with an upstream template the
 | 
				
			||||||
evaluation will be case sensitive. This setting defaults to false so only set it if you want 
 | 
					evaluation will be case sensitive. This setting defaults to false so only set it if you want 
 | 
				
			||||||
the ReRoute to be case sensitive is my advice!
 | 
					the ReRoute to be case sensitive is my advice!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Administration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ocelot supports changing configuration during runtime via an authenticated HTTP API. The API is authenticated 
 | 
				
			||||||
 | 
					using bearer tokens that you request from iteself. This is provided by the amazing [IdentityServer](https://github.com/IdentityServer/IdentityServer4)
 | 
				
			||||||
 | 
					project that I have been using for a few years now. Check them out.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In order to enable the administration section you need to do a few things. First of all add this to your
 | 
				
			||||||
 | 
					initial configuration.json. The value can be anything you want and it is obviously reccomended don't use
 | 
				
			||||||
 | 
					a url you would like to route through with Ocelot as this will not work. The administration uses the
 | 
				
			||||||
 | 
					MapWhen functionality of asp.net core and all requests to root/administration will be sent there not 
 | 
				
			||||||
 | 
					to the Ocelot middleware.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
 | 
					"GlobalConfiguration": {
 | 
				
			||||||
 | 
					    "AdministrationPath": "/administration"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					This will get the admin area set up but not the authentication. Please note that this is a very basic approach to 
 | 
				
			||||||
 | 
					this problem and if needed we can obviously improve on this!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You need to set 3 environmental variables. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    OCELOT_USERNAME
 | 
				
			||||||
 | 
					    OCELOT_HASH
 | 
				
			||||||
 | 
					    OCELOT_SALT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					These need to be the admin username you want to use with Ocelot and the hash and salt of the password you want to 
 | 
				
			||||||
 | 
					use given hashing algorythm. When requesting bearer tokens for use with the administration api you will need to
 | 
				
			||||||
 | 
					supply username and password.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In order to create a hash and salt of your password please check out HashCreationTests.should_create_hash_and_salt() 
 | 
				
			||||||
 | 
					this technique is based on [this](https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/consumer-apis/password-hashing)
 | 
				
			||||||
 | 
					using SHA256 rather than SHA1.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Now if you went with the configuration options above and want to access the API you can use the postman scripts
 | 
				
			||||||
 | 
					called ocelot.postman_collection.json in the solution to change the Ocelot configuration. Obviously these 
 | 
				
			||||||
 | 
					will need to be changed if you are running Ocelot on a different url to http://localhost:5000.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The scripts show you how to request a bearer token from ocelot and then use it to GET the existing configuration and POST 
 | 
				
			||||||
 | 
					a configuration.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Service Discovery
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ocelot allows you to specify a service discovery provider and will use this to find the host and port 
 | 
				
			||||||
 | 
					for the downstream service Ocelot is forwarding a request to. At the moment this is only supported in the
 | 
				
			||||||
 | 
					GlobalConfiguration section which means the same service discovery provider will be used for all ReRoutes
 | 
				
			||||||
 | 
					you specify a ServiceName for at ReRoute level. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In the future we can add a feature that allows ReRoute specfic configuration. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					At the moment the only supported service discovery provider is Consul. The following is required in the 
 | 
				
			||||||
 | 
					GlobalConfiguration. The Provider is required and if you do not specify a host and port the Consul default
 | 
				
			||||||
 | 
					will be used.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
 | 
					"ServiceDiscoveryProvider": {
 | 
				
			||||||
 | 
					    "Provider":"Consul",
 | 
				
			||||||
 | 
					    "Host":"localhost",
 | 
				
			||||||
 | 
					    "Port":8500
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In order to tell Ocelot a ReRoute is to use the service discovery provider for its host and port you must add the 
 | 
				
			||||||
 | 
					ServiceName and load balancer you wish to use when making requests downstream. At the moment Ocelot has a RoundRobin
 | 
				
			||||||
 | 
					and LeastConnection algorithm you can use. If no load balancer is specified Ocelot will not load balance requests.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    "DownstreamPathTemplate": "/api/posts/{postId}",
 | 
				
			||||||
 | 
					    "DownstreamScheme": "https",
 | 
				
			||||||
 | 
					    "UpstreamPathTemplate": "/posts/{postId}",
 | 
				
			||||||
 | 
					    "UpstreamHttpMethod": "Put",
 | 
				
			||||||
 | 
					    "ServiceName": "product",
 | 
				
			||||||
 | 
					    "LoadBalancer": "LeastConnection"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					When this is set up Ocelot will lookup the downstream host and port from the service discover provider and load balancer
 | 
				
			||||||
 | 
					requests across any available services.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Authentication
 | 
					## Authentication
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Ocelot currently supports the use of bearer tokens with Identity Server (more providers to
 | 
					Ocelot currently supports the use of bearer tokens with Identity Server (more providers to
 | 
				
			||||||
come if required). In order to identity a ReRoute as authenticated it needs the following
 | 
					come if required). In order to identity a ReRoute as authenticated it needs the following
 | 
				
			||||||
configuration added.
 | 
					configuration added.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
"AuthenticationOptions": {
 | 
					"AuthenticationOptions": {
 | 
				
			||||||
    "Provider": "IdentityServer",
 | 
					    "Provider": "IdentityServer",
 | 
				
			||||||
    "ProviderRootUrl": "http://localhost:52888",
 | 
					    "ProviderRootUrl": "http://localhost:52888",
 | 
				
			||||||
@@ -175,6 +293,7 @@ configuration added.
 | 
				
			|||||||
    ],
 | 
					    ],
 | 
				
			||||||
    "ScopeSecret": "secret"
 | 
					    "ScopeSecret": "secret"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
In this example the Provider is specified as IdentityServer. This string is important 
 | 
					In this example the Provider is specified as IdentityServer. This string is important 
 | 
				
			||||||
because it is used to identity the authentication provider (as previously mentioned in
 | 
					because it is used to identity the authentication provider (as previously mentioned in
 | 
				
			||||||
@@ -193,9 +312,11 @@ is 401 unauthorised.
 | 
				
			|||||||
Ocelot supports claims based authorisation which is run post authentication. This means if
 | 
					Ocelot supports claims based authorisation which is run post authentication. This means if
 | 
				
			||||||
you have a route you want to authorise you can add the following to you ReRoute configuration.
 | 
					you have a route you want to authorise you can add the following to you ReRoute configuration.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
"RouteClaimsRequirement": {
 | 
					"RouteClaimsRequirement": {
 | 
				
			||||||
    "UserType": "registered"
 | 
					    "UserType": "registered"
 | 
				
			||||||
},
 | 
					},
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
In this example when the authorisation middleware is called Ocelot will check to see
 | 
					In this example when the authorisation middleware is called Ocelot will check to see
 | 
				
			||||||
if the user has the claim type UserType and if the value of that claim is registered. 
 | 
					if the user has the claim type UserType and if the value of that claim is registered. 
 | 
				
			||||||
@@ -234,10 +355,12 @@ and add whatever was at the index requested to the transform.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Below is an example configuration that will transforms claims to claims
 | 
					Below is an example configuration that will transforms claims to claims
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
 "AddClaimsToRequest": {
 | 
					 "AddClaimsToRequest": {
 | 
				
			||||||
    "UserType": "Claims[sub] > value[0] > |",
 | 
					    "UserType": "Claims[sub] > value[0] > |",
 | 
				
			||||||
    "UserId": "Claims[sub] > value[1] > |"
 | 
					    "UserId": "Claims[sub] > value[1] > |"
 | 
				
			||||||
},
 | 
					},
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
This shows a transforms where Ocelot looks at the users sub claim and transforms it into
 | 
					This shows a transforms where Ocelot looks at the users sub claim and transforms it into
 | 
				
			||||||
UserType and UserId claims. Assuming the sub looks like this "usertypevalue|useridvalue".
 | 
					UserType and UserId claims. Assuming the sub looks like this "usertypevalue|useridvalue".
 | 
				
			||||||
@@ -246,9 +369,11 @@ UserType and UserId claims. Assuming the sub looks like this "usertypevalue|user
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Below is an example configuration that will transforms claims to headers
 | 
					Below is an example configuration that will transforms claims to headers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
"AddHeadersToRequest": {
 | 
					"AddHeadersToRequest": {
 | 
				
			||||||
    "CustomerId": "Claims[sub] > value[1] > |"
 | 
					    "CustomerId": "Claims[sub] > value[1] > |"
 | 
				
			||||||
},
 | 
					},
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
This shows a transform where Ocelot looks at the users sub claim and trasnforms it into a 
 | 
					This shows a transform where Ocelot looks at the users sub claim and trasnforms it into a 
 | 
				
			||||||
CustomerId header. Assuming the sub looks like this "usertypevalue|useridvalue".
 | 
					CustomerId header. Assuming the sub looks like this "usertypevalue|useridvalue".
 | 
				
			||||||
@@ -257,26 +382,36 @@ CustomerId header. Assuming the sub looks like this "usertypevalue|useridvalue".
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Below is an example configuration that will transforms claims to query string parameters
 | 
					Below is an example configuration that will transforms claims to query string parameters
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
"AddQueriesToRequest": {
 | 
					"AddQueriesToRequest": {
 | 
				
			||||||
    "LocationId": "Claims[LocationId] > value",
 | 
					    "LocationId": "Claims[LocationId] > value",
 | 
				
			||||||
},
 | 
					},
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
This shows a transform where Ocelot looks at the users LocationId claim and add its as
 | 
					This shows a transform where Ocelot looks at the users LocationId claim and add its as
 | 
				
			||||||
a query string parameter to be forwarded onto the downstream service.
 | 
					a query string parameter to be forwarded onto the downstream service.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Logging
 | 
					## Quality of Service
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Ocelot uses the standard logging interfaces ILoggerFactory / ILogger<T> at the moment. 
 | 
					Ocelot supports one QoS capability at the current time. You can set on a per ReRoute basis if you 
 | 
				
			||||||
This is encapsulated in  IOcelotLogger / IOcelotLoggerFactory with an implementation 
 | 
					want to use a circuit breaker when making requests to a downstream service. This uses the an awesome
 | 
				
			||||||
for the standard asp.net core logging stuff at the moment. 
 | 
					.NET library called Polly check them out [here](https://github.com/App-vNext/Polly).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
There are a bunch of debugging logs in the ocelot middlewares however I think the 
 | 
					Add the following section to a ReRoute configuration. 
 | 
				
			||||||
system probably needs more logging in the code it calls into. Other than the debugging
 | 
					 | 
				
			||||||
there is a global error handler that should catch any errors thrown and log them as errors.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
The reason for not just using bog standard framework logging is that I could not 
 | 
					```json
 | 
				
			||||||
work out how to override the request id that get's logged when setting IncludeScopes 
 | 
					"QoSOptions": {
 | 
				
			||||||
to true for logging settings. Nicely onto the next feature.
 | 
					    "ExceptionsAllowedBeforeBreaking":3,
 | 
				
			||||||
 | 
					    "DurationOfBreak":5,
 | 
				
			||||||
 | 
					    "TimeoutValue":5000
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You must set a number greater than 0 against ExceptionsAllowedBeforeBreaking for this rule to be 
 | 
				
			||||||
 | 
					implemented. Duration of break is how long the circuit breaker will stay open for after it is tripped.
 | 
				
			||||||
 | 
					TimeoutValue means ff a request takes more than 5 seconds it will automatically be timed out. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If you do not add a QoS section QoS will not be used.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## RequestId / CorrelationId
 | 
					## RequestId / CorrelationId
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -291,14 +426,18 @@ have an OcelotRequestId.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
In order to use the requestid feature in your ReRoute configuration add this setting
 | 
					In order to use the requestid feature in your ReRoute configuration add this setting
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
"RequestIdKey": "OcRequestId"
 | 
					"RequestIdKey": "OcRequestId"
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
In this example OcRequestId is the request header that contains the clients request id.
 | 
					In this example OcRequestId is the request header that contains the clients request id.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
There is also a setting in the GlobalConfiguration section which will override whatever has been
 | 
					There is also a setting in the GlobalConfiguration section which will override whatever has been
 | 
				
			||||||
set at ReRoute level for the request id. The setting is as fllows.
 | 
					set at ReRoute level for the request id. The setting is as fllows.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
"RequestIdKey": "OcRequestId",
 | 
					"RequestIdKey": "OcRequestId",
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
It behaves in exactly the same way as the ReRoute level RequestIdKey settings.
 | 
					It behaves in exactly the same way as the ReRoute level RequestIdKey settings.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -317,7 +456,9 @@ and setting a TTL in seconds to expire the cache. More to come!
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
In orde to use caching on a route in your ReRoute configuration add this setting.
 | 
					In orde to use caching on a route in your ReRoute configuration add this setting.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
"FileCacheOptions": { "TtlSeconds": 15 }
 | 
					"FileCacheOptions": { "TtlSeconds": 15 }
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
In this example ttl seconds is set to 15 which means the cache will expire after 15 seconds.
 | 
					In this example ttl seconds is set to 15 which means the cache will expire after 15 seconds.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -329,6 +470,7 @@ pipeline and you are using any of the following. Remove them and try again!
 | 
				
			|||||||
When setting up Ocelot in your Startup.cs you can provide some additonal middleware 
 | 
					When setting up Ocelot in your Startup.cs you can provide some additonal middleware 
 | 
				
			||||||
and override middleware. This is done as follos.
 | 
					and override middleware. This is done as follos.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```csharp
 | 
				
			||||||
var configuration = new OcelotMiddlewareConfiguration
 | 
					var configuration = new OcelotMiddlewareConfiguration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    PreErrorResponderMiddleware = async (ctx, next) =>
 | 
					    PreErrorResponderMiddleware = async (ctx, next) =>
 | 
				
			||||||
@@ -338,6 +480,7 @@ and override middleware. This is done as follos.
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
app.UseOcelot(configuration);
 | 
					app.UseOcelot(configuration);
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
In the example above the provided function will run before the first piece of Ocelot middleware. 
 | 
					In the example above the provided function will run before the first piece of Ocelot middleware. 
 | 
				
			||||||
This allows a user to supply any behaviours they want before and after the Ocelot pipeline has run.
 | 
					This allows a user to supply any behaviours they want before and after the Ocelot pipeline has run.
 | 
				
			||||||
@@ -363,6 +506,20 @@ http request before it is passed to Ocelots request creator.
 | 
				
			|||||||
Obviously you can just add middleware as normal before the call to app.UseOcelot() It cannot be added
 | 
					Obviously you can just add middleware as normal before the call to app.UseOcelot() It cannot be added
 | 
				
			||||||
after as Ocelot does not call the next middleware.
 | 
					after as Ocelot does not call the next middleware.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ocelot uses the standard logging interfaces ILoggerFactory / ILogger<T> at the moment. 
 | 
				
			||||||
 | 
					This is encapsulated in  IOcelotLogger / IOcelotLoggerFactory with an implementation 
 | 
				
			||||||
 | 
					for the standard asp.net core logging stuff at the moment. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					There are a bunch of debugging logs in the ocelot middlewares however I think the 
 | 
				
			||||||
 | 
					system probably needs more logging in the code it calls into. Other than the debugging
 | 
				
			||||||
 | 
					there is a global error handler that should catch any errors thrown and log them as errors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The reason for not just using bog standard framework logging is that I could not 
 | 
				
			||||||
 | 
					work out how to override the request id that get's logged when setting IncludeScopes 
 | 
				
			||||||
 | 
					to true for logging settings. Nicely onto the next feature.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Not supported
 | 
					## Not supported
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Ocelot does not support...
 | 
					Ocelot does not support...
 | 
				
			||||||
@@ -381,8 +538,11 @@ forwarded to the downstream service. Obviously this would break everything :(
 | 
				
			|||||||
and doesnt check the response is OK. I think the fact you can even call stuff
 | 
					and doesnt check the response is OK. I think the fact you can even call stuff
 | 
				
			||||||
that isnt available is annoying. Let alone it be null.
 | 
					that isnt available is annoying. Let alone it be null.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[ Get more details at **codescene.io**.](https://codescene.io/projects/697/jobs/latest-successful/results)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Coming up
 | 
					## Coming up
 | 
				
			||||||
 | 
					
 | 
				
			||||||
You can see what we are working on [here](https://github.com/TomPallister/Ocelot/projects/1)
 | 
					You can see what we are working on [here](https://github.com/TomPallister/Ocelot/projects/1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										564
									
								
								README.md.orig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										564
									
								
								README.md.orig
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,564 @@
 | 
				
			|||||||
 | 
					# Ocelot
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[](https://ci.appveyor.com/project/TomPallister/ocelot-fcfpb)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[](https://gitter.im/Ocelotey/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[ Get more details at **codescene.io**.](https://codescene.io/projects/697/jobs/latest-successful/results)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Attempt at a .NET Api Gateway
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This project is aimed at people using .NET running 
 | 
				
			||||||
 | 
					a micro services / service orientated architecture 
 | 
				
			||||||
 | 
					that need a unified point of entry into their system.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In particular I want easy integration with 
 | 
				
			||||||
 | 
					IdentityServer reference and bearer tokens. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					We have been unable to find this in my current workplace
 | 
				
			||||||
 | 
					without having to write our own Javascript middlewares 
 | 
				
			||||||
 | 
					to handle the IdentityServer reference tokens. We would
 | 
				
			||||||
 | 
					rather use the IdentityServer code that already exists
 | 
				
			||||||
 | 
					to do this.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ocelot is a bunch of middlewares in a specific order.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ocelot manipulates the HttpRequest object into a state specified by its configuration until 
 | 
				
			||||||
 | 
					it reaches a request builder middleware where it creates a HttpRequestMessage object which is 
 | 
				
			||||||
 | 
					used to make a request to a downstream service. The middleware that makes the request is 
 | 
				
			||||||
 | 
					the last thing in the Ocelot pipeline. It does not call the next middleware. 
 | 
				
			||||||
 | 
					The response from the downstream service is stored in a per request scoped repository 
 | 
				
			||||||
 | 
					and retrived as the requests goes back up the Ocelot pipeline. There is a piece of middleware 
 | 
				
			||||||
 | 
					that maps the HttpResponseMessage onto the HttpResponse object and that is returned to the client.
 | 
				
			||||||
 | 
					That is basically it with a bunch of other features.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Contributing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Pull requests, issues and commentary welcome! No special process just create a request and get in 
 | 
				
			||||||
 | 
					touch either via gitter or create an issue. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## How to install
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ocelot is designed to work with ASP.NET core only and is currently 
 | 
				
			||||||
 | 
					built to netcoreapp1.1 [this](https://docs.microsoft.com/en-us/dotnet/articles/standard/library) documentation may prove helpful when working out if Ocelot would be suitable for you.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Install Ocelot and it's dependecies using nuget. At the moment 
 | 
				
			||||||
 | 
					all we have is the pre version. Once we have something working in 
 | 
				
			||||||
 | 
					a half decent way we will drop a version.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`Install-Package Ocelot`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					All versions can be found [here](https://www.nuget.org/packages/Ocelot/)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Configuration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					An example configuration can be found [here](https://github.com/TomPallister/Ocelot/blob/develop/test/Ocelot.ManualTest/configuration.json) 
 | 
				
			||||||
 | 
					and an explained configuration can be found [here](https://github.com/TomPallister/Ocelot/blob/develop/configuration-explanation.txt). More detailed instructions to come on how to configure this.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					There are two sections to the configuration. An array of ReRoutes and a GlobalConfiguration. 
 | 
				
			||||||
 | 
					The ReRoutes are the objects that tell Ocelot how to treat an upstream request. The Global 
 | 
				
			||||||
 | 
					configuration is a bit hacky and allows overrides of ReRoute specific settings. It's useful
 | 
				
			||||||
 | 
					if you don't want to manage lots of ReRoute specific settings.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    "ReRoutes": [],
 | 
				
			||||||
 | 
					    "GlobalConfiguration": {}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					More information on how to use these options is below..
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Startup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					An example startup using a json file for configuration can be seen below. 
 | 
				
			||||||
 | 
					Currently this is the only way to get configuration into Ocelot.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<<<<<<< HEAD
 | 
				
			||||||
 | 
						public class Startup
 | 
				
			||||||
 | 
					=======
 | 
				
			||||||
 | 
					```csharp
 | 
				
			||||||
 | 
					public class Startup
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public Startup(IHostingEnvironment env)
 | 
				
			||||||
 | 
					>>>>>>> develop
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        var builder = new ConfigurationBuilder()
 | 
				
			||||||
 | 
					            .SetBasePath(env.ContentRootPath)
 | 
				
			||||||
 | 
					            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
 | 
				
			||||||
 | 
					            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
 | 
				
			||||||
 | 
					            .AddJsonFile("configuration.json")
 | 
				
			||||||
 | 
					            .AddEnvironmentVariables();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Configuration = builder.Build();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public IConfigurationRoot Configuration { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void ConfigureServices(IServiceCollection services)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Action<ConfigurationBuilderCachePart> settings = (x) =>
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            x.WithMicrosoftLogging(log =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					<<<<<<< HEAD
 | 
				
			||||||
 | 
					                x.WithMicrosoftLogging(log =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    log.AddConsole(LogLevel.Debug);
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .WithDictionaryHandle();
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            services.AddOcelotOutputCaching(settings);
 | 
				
			||||||
 | 
					            services.AddOcelot(Configuration);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await app.UseOcelot();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Then in your Program.cs you will want to have the following. This can be changed if you 
 | 
				
			||||||
 | 
					don't wan't to use the default url e.g. UseUrls(someUrls) and should work as long as you keep the WebHostBuilder registration.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            IWebHostBuilder builder = new WebHostBuilder();
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            builder.ConfigureServices(s => {
 | 
				
			||||||
 | 
					                s.AddSingleton(builder);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            builder.UseKestrel()
 | 
				
			||||||
 | 
					                .UseContentRoot(Directory.GetCurrentDirectory())
 | 
				
			||||||
 | 
					                .UseStartup<Startup>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var host = builder.Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            host.Run();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Sadly we need to inject the IWebHostBuilder interface to get the applications scheme, url and port later. I cannot 
 | 
				
			||||||
 | 
					find a better way of doing this at the moment without setting this in a static or some kind of config.
 | 
				
			||||||
 | 
					=======
 | 
				
			||||||
 | 
					                log.AddConsole(LogLevel.Debug);
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .WithDictionaryHandle();
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        services.AddOcelotOutputCaching(settings);
 | 
				
			||||||
 | 
					        services.AddOcelotFileConfiguration(Configuration);
 | 
				
			||||||
 | 
					        services.AddOcelot();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        app.UseOcelot();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					>>>>>>> develop
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This is pretty much all you need to get going.......more to come! 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Routing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ocelot's primary functionality is to take incomeing http requests and forward them on
 | 
				
			||||||
 | 
					to a downstream service. At the moment in the form of another http request (in the future
 | 
				
			||||||
 | 
					this could be any transport mechanism.). 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ocelot always adds a trailing slash to an UpstreamPathTemplate.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ocelot's describes the routing of one request to another as a ReRoute. In order to get 
 | 
				
			||||||
 | 
					anything working in Ocelot you need to set up a ReRoute in the configuration.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    "ReRoutes": [
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In order to set up a ReRoute you need to add one to the json array called ReRoutes like
 | 
				
			||||||
 | 
					the following.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    "DownstreamPathTemplate": "/api/posts/{postId}",
 | 
				
			||||||
 | 
					    "DownstreamScheme": "https",
 | 
				
			||||||
 | 
					    "DownstreamPort": 80,
 | 
				
			||||||
 | 
					    "DownstreamHost" "localhost"
 | 
				
			||||||
 | 
					    "UpstreamPathTemplate": "/posts/{postId}",
 | 
				
			||||||
 | 
					    "UpstreamHttpMethod": "Put"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The DownstreamPathTemplate,Scheme, Port and Host make the URL that this request will be forwarded to.
 | 
				
			||||||
 | 
					The UpstreamPathTemplate is the URL that Ocelot will use to identity which 
 | 
				
			||||||
 | 
					DownstreamPathTemplate to use for a given request. Finally the UpstreamHttpMethod is used so
 | 
				
			||||||
 | 
					Ocelot can distinguish between requests to the same URL and is obviously needed to work :)
 | 
				
			||||||
 | 
					In Ocelot you can add placeholders for variables to your Templates in the form of {something}.
 | 
				
			||||||
 | 
					The placeholder needs to be in both the DownstreamPathTemplate and UpstreamPathTemplate. If it is
 | 
				
			||||||
 | 
					Ocelot will attempt to replace the placeholder with the correct variable value from the 
 | 
				
			||||||
 | 
					Upstream URL when the request comes in.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					At the moment without any configuration Ocelot will default to all ReRoutes being case insensitive.
 | 
				
			||||||
 | 
					In order to change this you can specify on a per ReRoute basis the following setting.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
 | 
					"ReRouteIsCaseSensitive": true
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This means that when Ocelot tries to match the incoming upstream url with an upstream template the
 | 
				
			||||||
 | 
					evaluation will be case sensitive. This setting defaults to false so only set it if you want 
 | 
				
			||||||
 | 
					the ReRoute to be case sensitive is my advice!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Administration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ocelot supports changing configuration during runtime via an authenticated HTTP API. The API is authenticated 
 | 
				
			||||||
 | 
					using bearer tokens that you request from iteself. This is provided by the amazing [IdentityServer](https://github.com/IdentityServer/IdentityServer4)
 | 
				
			||||||
 | 
					project that I have been using for a few years now. Check them out.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In order to enable the administration section you need to do a few things. First of all add this to your
 | 
				
			||||||
 | 
					initial configuration.json. The value can be anything you want and it is obviously reccomended don't use
 | 
				
			||||||
 | 
					a url you would like to route through with Ocelot as this will not work. The administration uses the
 | 
				
			||||||
 | 
					MapWhen functionality of asp.net core and all requests to root/administration will be sent there not 
 | 
				
			||||||
 | 
					to the Ocelot middleware.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        "GlobalConfiguration": {
 | 
				
			||||||
 | 
					            "AdministrationPath": "/administration"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This will get the admin area set up but not the authentication. Please note that this is a very basic approach to 
 | 
				
			||||||
 | 
					this problem and if needed we can obviously improve on this!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You need to set 3 environmental variables. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    OCELOT_USERNAME
 | 
				
			||||||
 | 
					    OCELOT_HASH
 | 
				
			||||||
 | 
					    OCELOT_SALT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					These need to be the admin username you want to use with Ocelot and the hash and salt of the password you want to 
 | 
				
			||||||
 | 
					use given hashing algorythm. When requesting bearer tokens for use with the administration api you will need to
 | 
				
			||||||
 | 
					supply username and password.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In order to create a hash and salt of your password please check out HashCreationTests.should_create_hash_and_salt() 
 | 
				
			||||||
 | 
					this technique is based on [this](https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/consumer-apis/password-hashing)
 | 
				
			||||||
 | 
					using SHA256 rather than SHA1.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Now if you went with the configuration options above and want to access the API you can use the postman scripts
 | 
				
			||||||
 | 
					called ocelot.postman_collection.json in the solution to change the Ocelot configuration. Obviously these 
 | 
				
			||||||
 | 
					will need to be changed if you are running Ocelot on a different url to http://localhost:5000.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The scripts show you how to request a bearer token from ocelot and then use it to GET the existing configuration and POST 
 | 
				
			||||||
 | 
					a configuration.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Service Discovery
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ocelot allows you to specify a service discovery provider and will use this to find the host and port 
 | 
				
			||||||
 | 
					for the downstream service Ocelot is forwarding a request to. At the moment this is only supported in the
 | 
				
			||||||
 | 
					GlobalConfiguration section which means the same service discovery provider will be used for all ReRoutes
 | 
				
			||||||
 | 
					you specify a ServiceName for at ReRoute level. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In the future we can add a feature that allows ReRoute specfic configuration. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					At the moment the only supported service discovery provider is Consul. The following is required in the 
 | 
				
			||||||
 | 
					GlobalConfiguration. The Provider is required and if you do not specify a host and port the Consul default
 | 
				
			||||||
 | 
					will be used.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
 | 
					"ServiceDiscoveryProvider": {
 | 
				
			||||||
 | 
					    "Provider":"Consul",
 | 
				
			||||||
 | 
					    "Host":"localhost",
 | 
				
			||||||
 | 
					    "Port":8500
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In order to tell Ocelot a ReRoute is to use the service discovery provider for its host and port you must add the 
 | 
				
			||||||
 | 
					ServiceName and load balancer you wish to use when making requests downstream. At the moment Ocelot has a RoundRobin
 | 
				
			||||||
 | 
					and LeastConnection algorithm you can use. If no load balancer is specified Ocelot will not load balance requests.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    "DownstreamPathTemplate": "/api/posts/{postId}",
 | 
				
			||||||
 | 
					    "DownstreamScheme": "https",
 | 
				
			||||||
 | 
					    "UpstreamPathTemplate": "/posts/{postId}",
 | 
				
			||||||
 | 
					    "UpstreamHttpMethod": "Put",
 | 
				
			||||||
 | 
					    "ServiceName": "product",
 | 
				
			||||||
 | 
					    "LoadBalancer": "LeastConnection"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					When this is set up Ocelot will lookup the downstream host and port from the service discover provider and load balancer
 | 
				
			||||||
 | 
					requests across any available services.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Authentication
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ocelot currently supports the use of bearer tokens with Identity Server (more providers to
 | 
				
			||||||
 | 
					come if required). In order to identity a ReRoute as authenticated it needs the following
 | 
				
			||||||
 | 
					configuration added.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
 | 
					"AuthenticationOptions": {
 | 
				
			||||||
 | 
					    "Provider": "IdentityServer",
 | 
				
			||||||
 | 
					    "ProviderRootUrl": "http://localhost:52888",
 | 
				
			||||||
 | 
					    "ScopeName": "api",
 | 
				
			||||||
 | 
					    "AdditionalScopes": [
 | 
				
			||||||
 | 
					        "openid",
 | 
				
			||||||
 | 
					        "offline_access"
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "ScopeSecret": "secret"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In this example the Provider is specified as IdentityServer. This string is important 
 | 
				
			||||||
 | 
					because it is used to identity the authentication provider (as previously mentioned in
 | 
				
			||||||
 | 
					the future there might be more providers). Identity server requires that the client
 | 
				
			||||||
 | 
					talk to it so we need to provide the root url of the IdentityServer as ProviderRootUrl.
 | 
				
			||||||
 | 
					IdentityServer requires at least one scope and you can also provider additional scopes.
 | 
				
			||||||
 | 
					Finally if you are using IdentityServer reference tokens you need to provide the scope
 | 
				
			||||||
 | 
					secret. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ocelot will use this configuration to build an authentication handler and if 
 | 
				
			||||||
 | 
					authentication is succefull the next middleware will be called else the response
 | 
				
			||||||
 | 
					is 401 unauthorised.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Authorisation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ocelot supports claims based authorisation which is run post authentication. This means if
 | 
				
			||||||
 | 
					you have a route you want to authorise you can add the following to you ReRoute configuration.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
 | 
					"RouteClaimsRequirement": {
 | 
				
			||||||
 | 
					    "UserType": "registered"
 | 
				
			||||||
 | 
					},
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In this example when the authorisation middleware is called Ocelot will check to see
 | 
				
			||||||
 | 
					if the user has the claim type UserType and if the value of that claim is registered. 
 | 
				
			||||||
 | 
					If it isn't then the user will not be authorised and the response will be 403 forbidden.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Claims Tranformation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ocelot allows the user to access claims and transform them into headers, query string 
 | 
				
			||||||
 | 
					parameters and other claims. This is only available once a user has been authenticated.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					After the user is authenticated we run the claims to claims transformation middleware.
 | 
				
			||||||
 | 
					This allows the user to transform claims before the authorisation middleware is called.
 | 
				
			||||||
 | 
					After the user is authorised first we call the claims to headers middleware and Finally
 | 
				
			||||||
 | 
					the claims to query strig parameters middleware.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The syntax for performing the transforms is the same for each proces. In the ReRoute
 | 
				
			||||||
 | 
					configuration a json dictionary is added with a specific name either AddClaimsToRequest,
 | 
				
			||||||
 | 
					AddHeadersToRequest, AddQueriesToRequest. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Note I'm not a hotshot programmer so have no idea if this syntax is good..
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Within this dictionary the entries specify how Ocelot should transform things! 
 | 
				
			||||||
 | 
					The key to the dictionary is going to become the key of either a claim, header 
 | 
				
			||||||
 | 
					or query parameter.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The value of the entry is parsed to logic that will perform the transform. First of
 | 
				
			||||||
 | 
					all a dictionary accessor is specified e.g. Claims[CustomerId]. This means we want
 | 
				
			||||||
 | 
					to access the claims and get the CustomerId claim type. Next is a greater than (>)
 | 
				
			||||||
 | 
					symbol which is just used to split the string. The next entry is either value or value with
 | 
				
			||||||
 | 
					and indexer. If value is specifed Ocelot will just take the value and add it to the 
 | 
				
			||||||
 | 
					transform. If the value has an indexer Ocelot will look for a delimiter which is provided
 | 
				
			||||||
 | 
					after another greater than symbol. Ocelot will then split the value on the delimiter 
 | 
				
			||||||
 | 
					and add whatever was at the index requested to the transform.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### Claims to Claims Tranformation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Below is an example configuration that will transforms claims to claims
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
 | 
					 "AddClaimsToRequest": {
 | 
				
			||||||
 | 
					    "UserType": "Claims[sub] > value[0] > |",
 | 
				
			||||||
 | 
					    "UserId": "Claims[sub] > value[1] > |"
 | 
				
			||||||
 | 
					},
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This shows a transforms where Ocelot looks at the users sub claim and transforms it into
 | 
				
			||||||
 | 
					UserType and UserId claims. Assuming the sub looks like this "usertypevalue|useridvalue".
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### Claims to Headers Tranformation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Below is an example configuration that will transforms claims to headers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
 | 
					"AddHeadersToRequest": {
 | 
				
			||||||
 | 
					    "CustomerId": "Claims[sub] > value[1] > |"
 | 
				
			||||||
 | 
					},
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This shows a transform where Ocelot looks at the users sub claim and trasnforms it into a 
 | 
				
			||||||
 | 
					CustomerId header. Assuming the sub looks like this "usertypevalue|useridvalue".
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### Claims to Query String Parameters Tranformation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Below is an example configuration that will transforms claims to query string parameters
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
 | 
					"AddQueriesToRequest": {
 | 
				
			||||||
 | 
					    "LocationId": "Claims[LocationId] > value",
 | 
				
			||||||
 | 
					},
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This shows a transform where Ocelot looks at the users LocationId claim and add its as
 | 
				
			||||||
 | 
					a query string parameter to be forwarded onto the downstream service.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Quality of Service
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ocelot supports one QoS capability at the current time. You can set on a per ReRoute basis if you 
 | 
				
			||||||
 | 
					want to use a circuit breaker when making requests to a downstream service. This uses the an awesome
 | 
				
			||||||
 | 
					.NET library called Polly check them out [here](https://github.com/App-vNext/Polly).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Add the following section to a ReRoute configuration. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
 | 
					"QoSOptions": {
 | 
				
			||||||
 | 
					    "ExceptionsAllowedBeforeBreaking":3,
 | 
				
			||||||
 | 
					    "DurationOfBreak":5,
 | 
				
			||||||
 | 
					    "TimeoutValue":5000
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You must set a number greater than 0 against ExceptionsAllowedBeforeBreaking for this rule to be 
 | 
				
			||||||
 | 
					implemented. Duration of break is how long the circuit breaker will stay open for after it is tripped.
 | 
				
			||||||
 | 
					TimeoutValue means ff a request takes more than 5 seconds it will automatically be timed out. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If you do not add a QoS section QoS will not be used.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## RequestId / CorrelationId
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ocelot supports a client sending a request id in the form of a header. If set Ocelot will
 | 
				
			||||||
 | 
					use the requestid for logging as soon as it becomes available in the middleware pipeline. 
 | 
				
			||||||
 | 
					Ocelot will also forward the request id with the specified header to the downstream service.
 | 
				
			||||||
 | 
					I'm not sure if have this spot on yet in terms of the pipeline order becasue there are a few logs
 | 
				
			||||||
 | 
					that don't get the users request id at the moment and ocelot just logs not set for request id
 | 
				
			||||||
 | 
					which sucks. You can still get the framework request id in the logs if you set 
 | 
				
			||||||
 | 
					IncludeScopes true in your logging config. This can then be used to match up later logs that do
 | 
				
			||||||
 | 
					have an OcelotRequestId.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In order to use the requestid feature in your ReRoute configuration add this setting
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
 | 
					"RequestIdKey": "OcRequestId"
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In this example OcRequestId is the request header that contains the clients request id.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					There is also a setting in the GlobalConfiguration section which will override whatever has been
 | 
				
			||||||
 | 
					set at ReRoute level for the request id. The setting is as fllows.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
 | 
					"RequestIdKey": "OcRequestId",
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It behaves in exactly the same way as the ReRoute level RequestIdKey settings.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Caching 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ocelot supports some very rudimentary caching at the moment provider by 
 | 
				
			||||||
 | 
					the [CacheManager](http://cachemanager.net/) project. This is an amazing project
 | 
				
			||||||
 | 
					that is solving a lot of caching problems. I would reccomend using this package to 
 | 
				
			||||||
 | 
					cache with Ocelot. If you look at the example [here](https://github.com/TomPallister/Ocelot/blob/develop/test/Ocelot.ManualTest/Startup.cs)
 | 
				
			||||||
 | 
					you can see how the cache manager is setup and then passed into the Ocelot 
 | 
				
			||||||
 | 
					AddOcelotOutputCaching configuration method. You can use any settings supported by 
 | 
				
			||||||
 | 
					the CacheManager package and just pass them in.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Anyway Ocelot currently supports caching on the URL of the downstream service 
 | 
				
			||||||
 | 
					and setting a TTL in seconds to expire the cache. More to come!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In orde to use caching on a route in your ReRoute configuration add this setting.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```json
 | 
				
			||||||
 | 
					"FileCacheOptions": { "TtlSeconds": 15 }
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In this example ttl seconds is set to 15 which means the cache will expire after 15 seconds.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Ocelot Middleware injection and overrides
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Warning use with caution. If you are seeing any exceptions or strange behavior in your middleware 
 | 
				
			||||||
 | 
					pipeline and you are using any of the following. Remove them and try again!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					When setting up Ocelot in your Startup.cs you can provide some additonal middleware 
 | 
				
			||||||
 | 
					and override middleware. This is done as follos.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```csharp
 | 
				
			||||||
 | 
					var configuration = new OcelotMiddlewareConfiguration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    PreErrorResponderMiddleware = async (ctx, next) =>
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        await next.Invoke();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app.UseOcelot(configuration);
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In the example above the provided function will run before the first piece of Ocelot middleware. 
 | 
				
			||||||
 | 
					This allows a user to supply any behaviours they want before and after the Ocelot pipeline has run.
 | 
				
			||||||
 | 
					This means you can break everything so use at your own pleasure!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The user can set functions against the following.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					+ PreErrorResponderMiddleware - Already explained above.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					+ PreAuthenticationMiddleware - This allows the user to run pre authentication logic and then call
 | 
				
			||||||
 | 
					Ocelot's authentication middleware.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					+ AuthenticationMiddleware - This overrides Ocelots authentication middleware.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					+ PreAuthorisationMiddleware - This allows the user to run pre authorisation logic and then call
 | 
				
			||||||
 | 
					Ocelot's authorisation middleware.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					+ AuthorisationMiddleware - This overrides Ocelots authorisation middleware.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					+ PreQueryStringBuilderMiddleware - This alows the user to manipulate the query string on the 
 | 
				
			||||||
 | 
					http request before it is passed to Ocelots request creator.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Obviously you can just add middleware as normal before the call to app.UseOcelot() It cannot be added
 | 
				
			||||||
 | 
					after as Ocelot does not call the next middleware.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ocelot uses the standard logging interfaces ILoggerFactory / ILogger<T> at the moment. 
 | 
				
			||||||
 | 
					This is encapsulated in  IOcelotLogger / IOcelotLoggerFactory with an implementation 
 | 
				
			||||||
 | 
					for the standard asp.net core logging stuff at the moment. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					There are a bunch of debugging logs in the ocelot middlewares however I think the 
 | 
				
			||||||
 | 
					system probably needs more logging in the code it calls into. Other than the debugging
 | 
				
			||||||
 | 
					there is a global error handler that should catch any errors thrown and log them as errors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The reason for not just using bog standard framework logging is that I could not 
 | 
				
			||||||
 | 
					work out how to override the request id that get's logged when setting IncludeScopes 
 | 
				
			||||||
 | 
					to true for logging settings. Nicely onto the next feature.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Not supported
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ocelot does not support...
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					+ Chunked Encoding - Ocelot will always get the body size and return Content-Length 
 | 
				
			||||||
 | 
					header. Sorry if this doesn't work for your use case! 
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					+ Fowarding a host header - The host header that you send to Ocelot will not be 
 | 
				
			||||||
 | 
					forwarded to the downstream service. Obviously this would break everything :(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Things that are currently annoying me
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					+ The ReRoute configuration object is too large.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					+ The base OcelotMiddleware lets you access things that are going to be null
 | 
				
			||||||
 | 
					and doesnt check the response is OK. I think the fact you can even call stuff
 | 
				
			||||||
 | 
					that isnt available is annoying. Let alone it be null.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Coming up
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You can see what we are working on [here](https://github.com/TomPallister/Ocelot/projects/1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										1
									
								
								ReleaseNotes.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								ReleaseNotes.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					No issues closed since last release
 | 
				
			||||||
							
								
								
									
										12
									
								
								appveyor.yml
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								appveyor.yml
									
									
									
									
									
								
							@@ -1,12 +0,0 @@
 | 
				
			|||||||
version: 1.0.{build}
 | 
					 | 
				
			||||||
configuration:
 | 
					 | 
				
			||||||
- Release
 | 
					 | 
				
			||||||
platform: Any CPU
 | 
					 | 
				
			||||||
build_script:
 | 
					 | 
				
			||||||
- build.bat
 | 
					 | 
				
			||||||
test_script:
 | 
					 | 
				
			||||||
- run-tests.bat
 | 
					 | 
				
			||||||
after_test:
 | 
					 | 
				
			||||||
- push-to-nuget.bat %appveyor_build_version%-rc1 %nugetApiKey%
 | 
					 | 
				
			||||||
cache:
 | 
					 | 
				
			||||||
- '%USERPROFILE%\.nuget\packages'
 | 
					 | 
				
			||||||
							
								
								
									
										1
									
								
								build-and-release-unstable.ps1
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								build-and-release-unstable.ps1
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					./build.ps1 -target BuildAndReleaseUnstable
 | 
				
			||||||
@@ -1,2 +0,0 @@
 | 
				
			|||||||
./run-tests.bat
 | 
					 | 
				
			||||||
./build.bat
 | 
					 | 
				
			||||||
							
								
								
									
										1
									
								
								build-and-run-tests.ps1
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								build-and-run-tests.ps1
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					./build.ps1 -target RunTests
 | 
				
			||||||
@@ -1,8 +0,0 @@
 | 
				
			|||||||
echo -------------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
echo Building Ocelot
 | 
					 | 
				
			||||||
dotnet restore src/Ocelot
 | 
					 | 
				
			||||||
dotnet build src/Ocelot -c Release
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							
								
								
									
										388
									
								
								build.cake
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										388
									
								
								build.cake
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,388 @@
 | 
				
			|||||||
 | 
					#tool "nuget:?package=GitVersion.CommandLine"
 | 
				
			||||||
 | 
					#tool "nuget:?package=OpenCover"
 | 
				
			||||||
 | 
					#tool "nuget:?package=ReportGenerator"
 | 
				
			||||||
 | 
					#tool "nuget:?package=GitReleaseNotes"
 | 
				
			||||||
 | 
					#addin "nuget:?package=Cake.DoInDirectory"
 | 
				
			||||||
 | 
					#addin "nuget:?package=Cake.Json"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// compile
 | 
				
			||||||
 | 
					var compileConfig = Argument("configuration", "Release");
 | 
				
			||||||
 | 
					var projectJson = "./src/Ocelot/project.json";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// build artifacts
 | 
				
			||||||
 | 
					var artifactsDir = Directory("artifacts");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// unit testing
 | 
				
			||||||
 | 
					var artifactsForUnitTestsDir = artifactsDir + Directory("UnitTests");
 | 
				
			||||||
 | 
					var unitTestAssemblies = @"./test/Ocelot.UnitTests";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// acceptance testing
 | 
				
			||||||
 | 
					var artifactsForAcceptanceTestsDir = artifactsDir + Directory("AcceptanceTests");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// integration testing
 | 
				
			||||||
 | 
					var artifactsForIntegrationTestsDir = artifactsDir + Directory("IntegrationTests");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// benchmark testing
 | 
				
			||||||
 | 
					var artifactsForBenchmarkTestsDir = artifactsDir + Directory("BenchmarkTests");
 | 
				
			||||||
 | 
					var benchmarkTestAssemblies = @"./test/Ocelot.Benchmarks";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// packaging
 | 
				
			||||||
 | 
					var packagesDir = artifactsDir + Directory("Packages");
 | 
				
			||||||
 | 
					var releaseNotesFile = packagesDir + File("releasenotes.md");
 | 
				
			||||||
 | 
					var artifactsFile = packagesDir + File("artifacts.txt");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// unstable releases
 | 
				
			||||||
 | 
					var nugetFeedUnstableKey = EnvironmentVariable("nuget-apikey-unstable");
 | 
				
			||||||
 | 
					var nugetFeedUnstableUploadUrl = "https://www.nuget.org/api/v2/package";
 | 
				
			||||||
 | 
					var nugetFeedUnstableSymbolsUploadUrl = "https://www.nuget.org/api/v2/package";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// stable releases
 | 
				
			||||||
 | 
					var tagsUrl = "https://api.github.com/repos/tompallister/ocelot/releases/tags/";
 | 
				
			||||||
 | 
					var nugetFeedStableKey = EnvironmentVariable("nuget-apikey-stable");
 | 
				
			||||||
 | 
					var nugetFeedStableUploadUrl = "https://www.nuget.org/api/v2/package";
 | 
				
			||||||
 | 
					var nugetFeedStableSymbolsUploadUrl = "https://www.nuget.org/api/v2/package";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// internal build variables - don't change these.
 | 
				
			||||||
 | 
					var releaseTag = "";
 | 
				
			||||||
 | 
					string committedVersion = "0.0.0-dev";
 | 
				
			||||||
 | 
					var buildVersion = committedVersion;
 | 
				
			||||||
 | 
					GitVersion versioning = null;
 | 
				
			||||||
 | 
					var nugetFeedUnstableBranchFilter = "^(develop)$|^(PullRequest/)";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var target = Argument("target", "Default");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Information("target is " +target);
 | 
				
			||||||
 | 
					Information("Build configuration is " + compileConfig);	
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Task("Default")
 | 
				
			||||||
 | 
						.IsDependentOn("Build");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Task("Build")
 | 
				
			||||||
 | 
						.IsDependentOn("RunTests")
 | 
				
			||||||
 | 
						.IsDependentOn("CreatePackages");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Task("BuildAndReleaseUnstable")
 | 
				
			||||||
 | 
						.IsDependentOn("Build")
 | 
				
			||||||
 | 
						.IsDependentOn("ReleasePackagesToUnstableFeed");
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					Task("Clean")
 | 
				
			||||||
 | 
						.Does(() =>
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
					        if (DirectoryExists(artifactsDir))
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            DeleteDirectory(artifactsDir, recursive:true);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        CreateDirectory(artifactsDir);
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					Task("Version")
 | 
				
			||||||
 | 
						.Does(() =>
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							versioning = GetNuGetVersionForCommit();
 | 
				
			||||||
 | 
							var nugetVersion = versioning.NuGetVersion;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Information("SemVer version number: " + nugetVersion);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (AppVeyor.IsRunningOnAppVeyor)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Information("Persisting version number...");
 | 
				
			||||||
 | 
								PersistVersion(committedVersion, nugetVersion);
 | 
				
			||||||
 | 
								buildVersion = nugetVersion;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Information("We are not running on build server, so we won't persist the version number.");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Task("Restore")
 | 
				
			||||||
 | 
						.IsDependentOn("Clean")
 | 
				
			||||||
 | 
						.IsDependentOn("Version")
 | 
				
			||||||
 | 
						.Does(() =>
 | 
				
			||||||
 | 
						{	
 | 
				
			||||||
 | 
							DotNetCoreRestore("./src");
 | 
				
			||||||
 | 
							DotNetCoreRestore("./test");
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Task("RunUnitTests")
 | 
				
			||||||
 | 
						.IsDependentOn("Restore")
 | 
				
			||||||
 | 
						.Does(() =>
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							var buildSettings = new DotNetCoreTestSettings
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Configuration = compileConfig,
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							EnsureDirectoryExists(artifactsForUnitTestsDir);
 | 
				
			||||||
 | 
							DotNetCoreTest(unitTestAssemblies, buildSettings);
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Task("RunAcceptanceTests")
 | 
				
			||||||
 | 
						.IsDependentOn("Restore")
 | 
				
			||||||
 | 
						.Does(() =>
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							var buildSettings = new DotNetCoreTestSettings
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Configuration = "Debug", //acceptance test config is hard-coded for debug
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							EnsureDirectoryExists(artifactsForAcceptanceTestsDir);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							DoInDirectory("test/Ocelot.AcceptanceTests", () =>
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								DotNetCoreTest(".", buildSettings);
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Task("RunIntegrationTests")
 | 
				
			||||||
 | 
						.IsDependentOn("Restore")
 | 
				
			||||||
 | 
						.Does(() =>
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							var buildSettings = new DotNetCoreTestSettings
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Configuration = "Debug", //int test config is hard-coded for debug
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							EnsureDirectoryExists(artifactsForIntegrationTestsDir);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							DoInDirectory("test/Ocelot.IntegrationTests", () =>
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								DotNetCoreTest(".", buildSettings);
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Task("RunBenchmarkTests")
 | 
				
			||||||
 | 
						.IsDependentOn("Restore")
 | 
				
			||||||
 | 
						.Does(() =>
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							var buildSettings = new DotNetCoreRunSettings
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Configuration = compileConfig,
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							EnsureDirectoryExists(artifactsForBenchmarkTestsDir);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							DoInDirectory(benchmarkTestAssemblies, () =>
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								DotNetCoreRun(".", "", buildSettings);
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Task("RunTests")
 | 
				
			||||||
 | 
						.IsDependentOn("RunUnitTests")
 | 
				
			||||||
 | 
						.IsDependentOn("RunAcceptanceTests")
 | 
				
			||||||
 | 
						.IsDependentOn("RunIntegrationTests")
 | 
				
			||||||
 | 
						.Does(() =>
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Task("CreatePackages")
 | 
				
			||||||
 | 
						.Does(() => 
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							EnsureDirectoryExists(packagesDir);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
							GenerateReleaseNotes(releaseNotesFile);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var settings = new DotNetCorePackSettings
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									OutputDirectory = packagesDir,
 | 
				
			||||||
 | 
									NoBuild = true
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							DotNetCorePack(projectJson, settings);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        System.IO.File.WriteAllLines(artifactsFile, new[]{
 | 
				
			||||||
 | 
					            "nuget:Ocelot." + buildVersion + ".nupkg",
 | 
				
			||||||
 | 
					            "nugetSymbols:Ocelot." + buildVersion + ".symbols.nupkg",
 | 
				
			||||||
 | 
					            "releaseNotes:releasenotes.md"
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (AppVeyor.IsRunningOnAppVeyor)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								var path = packagesDir.ToString() + @"/**/*";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								foreach (var file in GetFiles(path))
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									AppVeyor.UploadArtifact(file.FullPath);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Task("ReleasePackagesToUnstableFeed")
 | 
				
			||||||
 | 
						.IsDependentOn("CreatePackages")
 | 
				
			||||||
 | 
						.Does(() =>
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (ShouldPublishToUnstableFeed(nugetFeedUnstableBranchFilter, versioning.BranchName))
 | 
				
			||||||
 | 
							{		
 | 
				
			||||||
 | 
								PublishPackages(packagesDir, artifactsFile, nugetFeedUnstableKey, nugetFeedUnstableUploadUrl, nugetFeedUnstableSymbolsUploadUrl);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Task("EnsureStableReleaseRequirements")
 | 
				
			||||||
 | 
					    .Does(() =>
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (!AppVeyor.IsRunningOnAppVeyor)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
					           throw new Exception("Stable release should happen via appveyor");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
							var isTag =
 | 
				
			||||||
 | 
					           AppVeyor.Environment.Repository.Tag.IsTag &&
 | 
				
			||||||
 | 
					           !string.IsNullOrWhiteSpace(AppVeyor.Environment.Repository.Tag.Name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!isTag)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
					           throw new Exception("Stable release should happen from a published GitHub release");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Task("UpdateVersionInfo")
 | 
				
			||||||
 | 
					    .IsDependentOn("EnsureStableReleaseRequirements")
 | 
				
			||||||
 | 
					    .Does(() =>
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        releaseTag = AppVeyor.Environment.Repository.Tag.Name;
 | 
				
			||||||
 | 
					        AppVeyor.UpdateBuildVersion(releaseTag);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Task("DownloadGitHubReleaseArtifacts")
 | 
				
			||||||
 | 
					    .IsDependentOn("UpdateVersionInfo")
 | 
				
			||||||
 | 
					    .Does(() =>
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        EnsureDirectoryExists(packagesDir);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var releaseUrl = tagsUrl + releaseTag;
 | 
				
			||||||
 | 
					        var assets_url = ParseJson(GetResource(releaseUrl))
 | 
				
			||||||
 | 
					            .GetValue("assets_url")
 | 
				
			||||||
 | 
								.Value<string>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        foreach(var asset in DeserializeJson<JArray>(GetResource(assets_url)))
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
								var file = packagesDir + File(asset.Value<string>("name"));
 | 
				
			||||||
 | 
								Information("Downloading " + file);
 | 
				
			||||||
 | 
					            DownloadFile(asset.Value<string>("browser_download_url"), file);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Task("ReleasePackagesToStableFeed")
 | 
				
			||||||
 | 
					    .IsDependentOn("DownloadGitHubReleaseArtifacts")
 | 
				
			||||||
 | 
					    .Does(() =>
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
							PublishPackages(packagesDir, artifactsFile, nugetFeedStableKey, nugetFeedStableUploadUrl, nugetFeedStableSymbolsUploadUrl);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Task("Release")
 | 
				
			||||||
 | 
					    .IsDependentOn("ReleasePackagesToStableFeed");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RunTarget(target);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Gets nuique nuget version for this commit
 | 
				
			||||||
 | 
					private GitVersion GetNuGetVersionForCommit()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    GitVersion(new GitVersionSettings{
 | 
				
			||||||
 | 
					        UpdateAssemblyInfo = false,
 | 
				
			||||||
 | 
					        OutputType = GitVersionOutput.BuildServer
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return GitVersion(new GitVersionSettings{ OutputType = GitVersionOutput.Json });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Updates project version in all of our projects
 | 
				
			||||||
 | 
					private void PersistVersion(string committedVersion, string newVersion)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						Information(string.Format("We'll search all project.json files for {0} and replace with {1}...", committedVersion, newVersion));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var projectJsonFiles = GetFiles("./**/project.json");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						foreach(var projectJsonFile in projectJsonFiles)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							var file = projectJsonFile.ToString();
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
							Information(string.Format("Updating {0}...", file));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var updatedProjectJson = System.IO.File.ReadAllText(file)
 | 
				
			||||||
 | 
								.Replace(committedVersion, newVersion);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							System.IO.File.WriteAllText(file, updatedProjectJson);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// generates release notes based on issues closed in GitHub since the last release
 | 
				
			||||||
 | 
					private void GenerateReleaseNotes(ConvertableFilePath file)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!IsRunningOnWindows())
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							Warning("We can't generate release notes as we're not running on Windows.");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Information("Generating release notes at " + file);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var releaseNotesExitCode = StartProcess(
 | 
				
			||||||
 | 
					        @"tools/GitReleaseNotes/tools/gitreleasenotes.exe", 
 | 
				
			||||||
 | 
					        new ProcessSettings { Arguments = ". /o " + file });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (string.IsNullOrEmpty(System.IO.File.ReadAllText(file)))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
					        System.IO.File.WriteAllText(file, "No issues closed since last release");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (releaseNotesExitCode != 0) 
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							throw new Exception("Failed to generate release notes");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Publishes code and symbols packages to nuget feed, based on contents of artifacts file
 | 
				
			||||||
 | 
					private void PublishPackages(ConvertableDirectoryPath packagesDir, ConvertableFilePath artifactsFile, string feedApiKey, string codeFeedUrl, string symbolFeedUrl)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					        var artifacts = System.IO.File
 | 
				
			||||||
 | 
					            .ReadAllLines(artifactsFile)
 | 
				
			||||||
 | 
					            .Select(l => l.Split(':'))
 | 
				
			||||||
 | 
					            .ToDictionary(v => v[0], v => v[1]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var codePackage = packagesDir + File(artifacts["nuget"]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Information("Pushing package " + codePackage);
 | 
				
			||||||
 | 
					        NuGetPush(
 | 
				
			||||||
 | 
					            codePackage,
 | 
				
			||||||
 | 
					            new NuGetPushSettings {
 | 
				
			||||||
 | 
					                ApiKey = feedApiKey,
 | 
				
			||||||
 | 
					                Source = codeFeedUrl
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// gets the resource from the specified url
 | 
				
			||||||
 | 
					private string GetResource(string url)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						Information("Getting resource from " + url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var assetsRequest = System.Net.WebRequest.CreateHttp(url);
 | 
				
			||||||
 | 
					    assetsRequest.Method = "GET";
 | 
				
			||||||
 | 
					    assetsRequest.Accept = "application/vnd.github.v3+json";
 | 
				
			||||||
 | 
					    assetsRequest.UserAgent = "BuildScript";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    using (var assetsResponse = assetsRequest.GetResponse())
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        var assetsStream = assetsResponse.GetResponseStream();
 | 
				
			||||||
 | 
					        var assetsReader = new StreamReader(assetsStream);
 | 
				
			||||||
 | 
					        return assetsReader.ReadToEnd();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private bool ShouldPublishToUnstableFeed(string filter, string branchName)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						var regex = new System.Text.RegularExpressions.Regex(filter);
 | 
				
			||||||
 | 
						var publish = regex.IsMatch(branchName);
 | 
				
			||||||
 | 
						if (publish)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							Information("Branch " + branchName + " will be published to the unstable feed");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							Information("Branch " + branchName + " will not be published to the unstable feed");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return publish;	
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										189
									
								
								build.ps1
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								build.ps1
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,189 @@
 | 
				
			|||||||
 | 
					##########################################################################
 | 
				
			||||||
 | 
					# This is the Cake bootstrapper script for PowerShell.
 | 
				
			||||||
 | 
					# This file was downloaded from https://github.com/cake-build/resources
 | 
				
			||||||
 | 
					# Feel free to change this file to fit your needs.
 | 
				
			||||||
 | 
					##########################################################################
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.SYNOPSIS
 | 
				
			||||||
 | 
					This is a Powershell script to bootstrap a Cake build.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.DESCRIPTION
 | 
				
			||||||
 | 
					This Powershell script will download NuGet if missing, restore NuGet tools (including Cake)
 | 
				
			||||||
 | 
					and execute your Cake build script with the parameters you provide.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.PARAMETER Script
 | 
				
			||||||
 | 
					The build script to execute.
 | 
				
			||||||
 | 
					.PARAMETER Target
 | 
				
			||||||
 | 
					The build script target to run.
 | 
				
			||||||
 | 
					.PARAMETER Configuration
 | 
				
			||||||
 | 
					The build configuration to use.
 | 
				
			||||||
 | 
					.PARAMETER Verbosity
 | 
				
			||||||
 | 
					Specifies the amount of information to be displayed.
 | 
				
			||||||
 | 
					.PARAMETER Experimental
 | 
				
			||||||
 | 
					Tells Cake to use the latest Roslyn release.
 | 
				
			||||||
 | 
					.PARAMETER WhatIf
 | 
				
			||||||
 | 
					Performs a dry run of the build script.
 | 
				
			||||||
 | 
					No tasks will be executed.
 | 
				
			||||||
 | 
					.PARAMETER Mono
 | 
				
			||||||
 | 
					Tells Cake to use the Mono scripting engine.
 | 
				
			||||||
 | 
					.PARAMETER SkipToolPackageRestore
 | 
				
			||||||
 | 
					Skips restoring of packages.
 | 
				
			||||||
 | 
					.PARAMETER ScriptArgs
 | 
				
			||||||
 | 
					Remaining arguments are added here.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.LINK
 | 
				
			||||||
 | 
					http://cakebuild.net
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[CmdletBinding()]
 | 
				
			||||||
 | 
					Param(
 | 
				
			||||||
 | 
					    [string]$Script = "build.cake",
 | 
				
			||||||
 | 
					    [string]$Target = "Default",
 | 
				
			||||||
 | 
					    [ValidateSet("Release", "Debug")]
 | 
				
			||||||
 | 
					    [string]$Configuration = "Release",
 | 
				
			||||||
 | 
					    [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")]
 | 
				
			||||||
 | 
					    [string]$Verbosity = "Verbose",
 | 
				
			||||||
 | 
					    [switch]$Experimental,
 | 
				
			||||||
 | 
					    [Alias("DryRun","Noop")]
 | 
				
			||||||
 | 
					    [switch]$WhatIf,
 | 
				
			||||||
 | 
					    [switch]$Mono,
 | 
				
			||||||
 | 
					    [switch]$SkipToolPackageRestore,
 | 
				
			||||||
 | 
					    [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
 | 
				
			||||||
 | 
					    [string[]]$ScriptArgs
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null
 | 
				
			||||||
 | 
					function MD5HashFile([string] $filePath)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if ([string]::IsNullOrEmpty($filePath) -or !(Test-Path $filePath -PathType Leaf))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $null
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [System.IO.Stream] $file = $null;
 | 
				
			||||||
 | 
					    [System.Security.Cryptography.MD5] $md5 = $null;
 | 
				
			||||||
 | 
					    try
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $md5 = [System.Security.Cryptography.MD5]::Create()
 | 
				
			||||||
 | 
					        $file = [System.IO.File]::OpenRead($filePath)
 | 
				
			||||||
 | 
					        return [System.BitConverter]::ToString($md5.ComputeHash($file))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    finally
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if ($file -ne $null)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            $file.Dispose()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Write-Host "Preparing to run build script..."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if(!$PSScriptRoot){
 | 
				
			||||||
 | 
					    $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$TOOLS_DIR = Join-Path $PSScriptRoot "tools"
 | 
				
			||||||
 | 
					$NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe"
 | 
				
			||||||
 | 
					$CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe"
 | 
				
			||||||
 | 
					$NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe"
 | 
				
			||||||
 | 
					$PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config"
 | 
				
			||||||
 | 
					$PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Should we use mono?
 | 
				
			||||||
 | 
					$UseMono = "";
 | 
				
			||||||
 | 
					if($Mono.IsPresent) {
 | 
				
			||||||
 | 
					    Write-Verbose -Message "Using the Mono based scripting engine."
 | 
				
			||||||
 | 
					    $UseMono = "-mono"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Should we use the new Roslyn?
 | 
				
			||||||
 | 
					$UseExperimental = "";
 | 
				
			||||||
 | 
					if($Experimental.IsPresent -and !($Mono.IsPresent)) {
 | 
				
			||||||
 | 
					    Write-Verbose -Message "Using experimental version of Roslyn."
 | 
				
			||||||
 | 
					    $UseExperimental = "-experimental"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Is this a dry run?
 | 
				
			||||||
 | 
					$UseDryRun = "";
 | 
				
			||||||
 | 
					if($WhatIf.IsPresent) {
 | 
				
			||||||
 | 
					    $UseDryRun = "-dryrun"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Make sure tools folder exists
 | 
				
			||||||
 | 
					if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) {
 | 
				
			||||||
 | 
					    Write-Verbose -Message "Creating tools directory..."
 | 
				
			||||||
 | 
					    New-Item -Path $TOOLS_DIR -Type directory | out-null
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Make sure that packages.config exist.
 | 
				
			||||||
 | 
					if (!(Test-Path $PACKAGES_CONFIG)) {
 | 
				
			||||||
 | 
					    Write-Verbose -Message "Downloading packages.config..."
 | 
				
			||||||
 | 
					    try { (New-Object System.Net.WebClient).DownloadFile("http://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) } catch {
 | 
				
			||||||
 | 
					        Throw "Could not download packages.config."
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Try find NuGet.exe in path if not exists
 | 
				
			||||||
 | 
					if (!(Test-Path $NUGET_EXE)) {
 | 
				
			||||||
 | 
					    Write-Verbose -Message "Trying to find nuget.exe in PATH..."
 | 
				
			||||||
 | 
					    $existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_) }
 | 
				
			||||||
 | 
					    $NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1
 | 
				
			||||||
 | 
					    if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) {
 | 
				
			||||||
 | 
					        Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)."
 | 
				
			||||||
 | 
					        $NUGET_EXE = $NUGET_EXE_IN_PATH.FullName
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Try download NuGet.exe if not exists
 | 
				
			||||||
 | 
					if (!(Test-Path $NUGET_EXE)) {
 | 
				
			||||||
 | 
					    Write-Verbose -Message "Downloading NuGet.exe..."
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					        (New-Object System.Net.WebClient).DownloadFile($NUGET_URL, $NUGET_EXE)
 | 
				
			||||||
 | 
					    } catch {
 | 
				
			||||||
 | 
					        Throw "Could not download NuGet.exe."
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Save nuget.exe path to environment to be available to child processed
 | 
				
			||||||
 | 
					$ENV:NUGET_EXE = $NUGET_EXE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Restore tools from NuGet?
 | 
				
			||||||
 | 
					if(-Not $SkipToolPackageRestore.IsPresent) {
 | 
				
			||||||
 | 
					    Push-Location
 | 
				
			||||||
 | 
					    Set-Location $TOOLS_DIR
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Check for changes in packages.config and remove installed tools if true.
 | 
				
			||||||
 | 
					    [string] $md5Hash = MD5HashFile($PACKAGES_CONFIG)
 | 
				
			||||||
 | 
					    if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or
 | 
				
			||||||
 | 
					      ($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) {
 | 
				
			||||||
 | 
					        Write-Verbose -Message "Missing or changed package.config hash..."
 | 
				
			||||||
 | 
					        Remove-Item * -Recurse -Exclude packages.config,nuget.exe
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Write-Verbose -Message "Restoring tools from NuGet..."
 | 
				
			||||||
 | 
					    $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if ($LASTEXITCODE -ne 0) {
 | 
				
			||||||
 | 
					        Throw "An error occured while restoring NuGet tools."
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    Write-Verbose -Message ($NuGetOutput | out-string)
 | 
				
			||||||
 | 
					    Pop-Location
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Make sure that Cake has been installed.
 | 
				
			||||||
 | 
					if (!(Test-Path $CAKE_EXE)) {
 | 
				
			||||||
 | 
					    Throw "Could not find Cake.exe at $CAKE_EXE"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Start Cake
 | 
				
			||||||
 | 
					Write-Host "Running build script..."
 | 
				
			||||||
 | 
					Invoke-Expression "& `"$CAKE_EXE`" `"$Script`" -target=`"$Target`" -configuration=`"$Configuration`" -verbosity=`"$Verbosity`" $UseMono $UseDryRun $UseExperimental $ScriptArgs"
 | 
				
			||||||
 | 
					exit $LASTEXITCODE
 | 
				
			||||||
							
								
								
									
										22
									
								
								build.readme.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								build.readme.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					#1. Overview
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This document summarises the build and release process for the project. The build scripts are written using [Cake](http://cakebuild.net/), and are defined in `./build.cake`. The scripts have been designed to be run by either developers locally or by a build server (currently [AppVeyor](https://www.appveyor.com/)), with minimal logic defined in the build server itself.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#2. Building
 | 
				
			||||||
 | 
					  * You'll generally want to run the `./build.ps1` script. This will compile, run unit and acceptance tests and build the output packages locally. Output will got to the `./artifacts` directory.
 | 
				
			||||||
 | 
					  * You can view the current commit's [SemVer](http://semver.org/) build information by running `./version.ps1`.
 | 
				
			||||||
 | 
					  * The other `./*.ps1` scripts perform subsets of the build process, if you don't want to run the full build.
 | 
				
			||||||
 | 
					  * The release process works best with GitFlow branching; this allows us to publish every development commit to an unstable feed with a unique SemVer version, and then choose when to release to a stable feed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#3. Release process
 | 
				
			||||||
 | 
					This section defines the release process for the maintainers of the project.
 | 
				
			||||||
 | 
					  * Merge pull requests to the `release` branch.
 | 
				
			||||||
 | 
					  * Every commit pushed to the Origin repo will kick off the [ocelot-build](https://ci.appveyor.com/project/binarymash/ocelot) project in AppVeyor. This performs the same tasks as the command line build, and in addition pushes the packages to the unstable nuget feed.
 | 
				
			||||||
 | 
					  * When you're ready for a release, create a release branch. You'll probably want to update the committed `./ReleaseNotes.md` based on the contents of the equivalent file in the `./artifacts` directory.
 | 
				
			||||||
 | 
					  * When the `release` branch has built successfully in Appveyor, select the build and then Deploy to the `GitHub Release` environment. This will create a new release in GitHub.
 | 
				
			||||||
 | 
					  * In Github, navigate to the [release](https://github.com/binarymash/Ocelot/releases). Modify the release name and tag as desired.
 | 
				
			||||||
 | 
					  * When you're ready, publish the release. This will tag the commit with the specified release number.
 | 
				
			||||||
 | 
					  * The [ocelot-release](https://ci.appveyor.com/project/binarymash/ocelot-wtaj9) project will detect the newly created tag and kick off the release process. This will download the artifacts from GitHub, and publish the packages to the stable nuget feed.
 | 
				
			||||||
 | 
					  * When you have a final stable release build, merge the `release` branch into `master` and `develop`. Deploy the master branch to github and following the full release process as described above. Don't forget to uncheck the "This is a pre-release" checkbox in GitHub before publishing.
 | 
				
			||||||
 | 
					  * Note - because the release builds are initiated by tagging a commit, if for some reason a release build fails in AppVeyor you'll need to delete the tag from the repo and republish the release in GitHub.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										101
									
								
								build.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										101
									
								
								build.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,101 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					##########################################################################
 | 
				
			||||||
 | 
					# This is the Cake bootstrapper script for Linux and OS X.
 | 
				
			||||||
 | 
					# This file was downloaded from https://github.com/cake-build/resources
 | 
				
			||||||
 | 
					# Feel free to change this file to fit your needs.
 | 
				
			||||||
 | 
					##########################################################################
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Define directories.
 | 
				
			||||||
 | 
					SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
 | 
				
			||||||
 | 
					TOOLS_DIR=$SCRIPT_DIR/tools
 | 
				
			||||||
 | 
					NUGET_EXE=$TOOLS_DIR/nuget.exe
 | 
				
			||||||
 | 
					CAKE_EXE=$TOOLS_DIR/Cake/Cake.exe
 | 
				
			||||||
 | 
					PACKAGES_CONFIG=$TOOLS_DIR/packages.config
 | 
				
			||||||
 | 
					PACKAGES_CONFIG_MD5=$TOOLS_DIR/packages.config.md5sum
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Define md5sum or md5 depending on Linux/OSX
 | 
				
			||||||
 | 
					MD5_EXE=
 | 
				
			||||||
 | 
					if [[ "$(uname -s)" == "Darwin" ]]; then
 | 
				
			||||||
 | 
					    MD5_EXE="md5 -r"
 | 
				
			||||||
 | 
					else
 | 
				
			||||||
 | 
					    MD5_EXE="md5sum"
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Define default arguments.
 | 
				
			||||||
 | 
					SCRIPT="build.cake"
 | 
				
			||||||
 | 
					TARGET="Default"
 | 
				
			||||||
 | 
					CONFIGURATION="Release"
 | 
				
			||||||
 | 
					VERBOSITY="verbose"
 | 
				
			||||||
 | 
					DRYRUN=
 | 
				
			||||||
 | 
					SHOW_VERSION=false
 | 
				
			||||||
 | 
					SCRIPT_ARGUMENTS=()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Parse arguments.
 | 
				
			||||||
 | 
					for i in "$@"; do
 | 
				
			||||||
 | 
					    case $1 in
 | 
				
			||||||
 | 
					        -s|--script) SCRIPT="$2"; shift ;;
 | 
				
			||||||
 | 
					        -t|--target) TARGET="$2"; shift ;;
 | 
				
			||||||
 | 
					        -c|--configuration) CONFIGURATION="$2"; shift ;;
 | 
				
			||||||
 | 
					        -v|--verbosity) VERBOSITY="$2"; shift ;;
 | 
				
			||||||
 | 
					        -d|--dryrun) DRYRUN="-dryrun" ;;
 | 
				
			||||||
 | 
					        --version) SHOW_VERSION=true ;;
 | 
				
			||||||
 | 
					        --) shift; SCRIPT_ARGUMENTS+=("$@"); break ;;
 | 
				
			||||||
 | 
					        *) SCRIPT_ARGUMENTS+=("$1") ;;
 | 
				
			||||||
 | 
					    esac
 | 
				
			||||||
 | 
					    shift
 | 
				
			||||||
 | 
					done
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Make sure the tools folder exist.
 | 
				
			||||||
 | 
					if [ ! -d "$TOOLS_DIR" ]; then
 | 
				
			||||||
 | 
					  mkdir "$TOOLS_DIR"
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Make sure that packages.config exist.
 | 
				
			||||||
 | 
					if [ ! -f "$TOOLS_DIR/packages.config" ]; then
 | 
				
			||||||
 | 
					    echo "Downloading packages.config..."
 | 
				
			||||||
 | 
					    curl -Lsfo "$TOOLS_DIR/packages.config" http://cakebuild.net/download/bootstrapper/packages
 | 
				
			||||||
 | 
					    if [ $? -ne 0 ]; then
 | 
				
			||||||
 | 
					        echo "An error occured while downloading packages.config."
 | 
				
			||||||
 | 
					        exit 1
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Download NuGet if it does not exist.
 | 
				
			||||||
 | 
					if [ ! -f "$NUGET_EXE" ]; then
 | 
				
			||||||
 | 
					    echo "Downloading NuGet..."
 | 
				
			||||||
 | 
					    curl -Lsfo "$NUGET_EXE" https://dist.nuget.org/win-x86-commandline/latest/nuget.exe
 | 
				
			||||||
 | 
					    if [ $? -ne 0 ]; then
 | 
				
			||||||
 | 
					        echo "An error occured while downloading nuget.exe."
 | 
				
			||||||
 | 
					        exit 1
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Restore tools from NuGet.
 | 
				
			||||||
 | 
					pushd "$TOOLS_DIR" >/dev/null
 | 
				
			||||||
 | 
					if [ ! -f "$PACKAGES_CONFIG_MD5" ] || [ "$( cat "$PACKAGES_CONFIG_MD5" | sed 's/\r$//' )" != "$( $MD5_EXE "$PACKAGES_CONFIG" | awk '{ print $1 }' )" ]; then
 | 
				
			||||||
 | 
					    find . -type d ! -name . | xargs rm -rf
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mono "$NUGET_EXE" install -ExcludeVersion
 | 
				
			||||||
 | 
					if [ $? -ne 0 ]; then
 | 
				
			||||||
 | 
					    echo "Could not restore NuGet packages."
 | 
				
			||||||
 | 
					    exit 1
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$MD5_EXE "$PACKAGES_CONFIG" | awk '{ print $1 }' >| "$PACKAGES_CONFIG_MD5"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					popd >/dev/null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Make sure that Cake has been installed.
 | 
				
			||||||
 | 
					if [ ! -f "$CAKE_EXE" ]; then
 | 
				
			||||||
 | 
					    echo "Could not find Cake.exe at '$CAKE_EXE'."
 | 
				
			||||||
 | 
					    exit 1
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Start Cake
 | 
				
			||||||
 | 
					if $SHOW_VERSION; then
 | 
				
			||||||
 | 
					    exec mono "$CAKE_EXE" -version
 | 
				
			||||||
 | 
					else
 | 
				
			||||||
 | 
					    exec mono "$CAKE_EXE" $SCRIPT -verbosity=$VERBOSITY -configuration=$CONFIGURATION -target=$TARGET $DRYRUN "${SCRIPT_ARGUMENTS[@]}"
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
@@ -1,12 +1,21 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	"ReRoutes": [
 | 
						"ReRoutes": [
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			# The url we are forwarding the request to, ocelot will not add a trailing slash
 | 
								# The downstream path we are forwarding the request to, ocelot will not add a trailing slash.
 | 
				
			||||||
			"DownstreamTemplate": "http://somehost.com/identityserverexample",
 | 
								# Ocelot replaces any placeholders {etc} with matched values from the incoming request.
 | 
				
			||||||
			# The path we are listening on for this re route, Ocelot will add a trailing slash to
 | 
								"DownstreamPathTemplate": "/identityserverexample/{someid}/something",
 | 
				
			||||||
			# this property. Then when a request is made Ocelot makes sure a trailing slash is added
 | 
								# The scheme you want Ocelot to use when making the downstream request
 | 
				
			||||||
			# to that so everything matches
 | 
								"DownstreamScheme": "https",
 | 
				
			||||||
			"UpstreamTemplate": "/identityserverexample",
 | 
								# The port you want Ocelot to use when making the downstream request, will default to 
 | 
				
			||||||
 | 
								# scheme if nothing set
 | 
				
			||||||
 | 
								"DownstreamPort": 80,
 | 
				
			||||||
 | 
								# The host address of the downstream service, should not have a trailing slash or scheme
 | 
				
			||||||
 | 
								# if there is a trailing slash Ocelot will remove it.
 | 
				
			||||||
 | 
								"DownstreamHost" "localhost"
 | 
				
			||||||
 | 
								# The path template we are listening on for this re route, Ocelot will add a trailing 
 | 
				
			||||||
 | 
								# slash to this property. Then when a request is made Ocelot makes sure a trailing 
 | 
				
			||||||
 | 
								# slash is added, so everything matches
 | 
				
			||||||
 | 
								"UpstreamPathTemplate": "/identityserverexample",
 | 
				
			||||||
			# The method we are listening for on this re route
 | 
								# The method we are listening for on this re route
 | 
				
			||||||
			"UpstreamHttpMethod": "Get",
 | 
								"UpstreamHttpMethod": "Get",
 | 
				
			||||||
			# Only support identity server at the moment
 | 
								# Only support identity server at the moment
 | 
				
			||||||
@@ -71,12 +80,24 @@
 | 
				
			|||||||
			# the caching a lot.
 | 
								# the caching a lot.
 | 
				
			||||||
			"FileCacheOptions": { "TtlSeconds": 15 },
 | 
								"FileCacheOptions": { "TtlSeconds": 15 },
 | 
				
			||||||
			# The value of this is used when matching the upstream template to an upstream url. 
 | 
								# The value of this is used when matching the upstream template to an upstream url. 
 | 
				
			||||||
			"ReRouteIsCaseSensitive": false
 | 
								"ReRouteIsCaseSensitive": false,
 | 
				
			||||||
 | 
								# Tells Ocelot the name of the service it is looking when making requests to service discovery
 | 
				
			||||||
 | 
								# for hosts and ports
 | 
				
			||||||
 | 
								"ServiceName": "product"
 | 
				
			||||||
 | 
								# Tells Ocelot which load balancer to use when making downstream requests.
 | 
				
			||||||
 | 
					            "LoadBalancer": "RoundRobin"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		# This section is meant to be for global configuration settings
 | 
							# This section is meant to be for global configuration settings
 | 
				
			||||||
		"GlobalConfiguration": {
 | 
							"GlobalConfiguration": {
 | 
				
			||||||
			# If this is set it will override any route specific request id keys, behaves the same
 | 
								# If this is set it will override any route specific request id keys, behaves the same
 | 
				
			||||||
			# otherwise
 | 
								# otherwise
 | 
				
			||||||
			"RequestIdKey": "OcRequestId",
 | 
								"RequestIdKey": "OcRequestId",
 | 
				
			||||||
 | 
								# If set Ocelot will try and use service discovery to locate downstream hosts and ports
 | 
				
			||||||
 | 
								 "ServiceDiscoveryProvider":
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									"Provider":"Consul",
 | 
				
			||||||
 | 
									"Host":"localhost",
 | 
				
			||||||
 | 
									"Port":8500
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
							
								
								
									
										1
									
								
								configuration.json
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										1
									
								
								configuration.json
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					{"ReRoutes":[],"GlobalConfiguration":{"RequestIdKey":null,"ServiceDiscoveryProvider":{"Provider":null,"Host":null,"Port":0},"AdministrationPath":"/administration"}}
 | 
				
			||||||
							
								
								
									
										3
									
								
								configuration.yaml
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										3
									
								
								configuration.yaml
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					Routes:
 | 
				
			||||||
 | 
					- Downstream: http://localhost:51879/
 | 
				
			||||||
 | 
					  Upstream: /heee
 | 
				
			||||||
							
								
								
									
										117
									
								
								ocelot.postman_collection.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								ocelot.postman_collection.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -1,11 +0,0 @@
 | 
				
			|||||||
echo -------------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
echo Packing Ocelot Version %1
 | 
					 | 
				
			||||||
nuget pack .\Ocelot.nuspec -version %1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
echo Publishing Ocelot
 | 
					 | 
				
			||||||
 nuget push Ocelot.%1.nupkg -ApiKey %2 -Source https://www.nuget.org/api/v2/package
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							
								
								
									
										1
									
								
								release.ps1
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								release.ps1
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					./build.ps1 -target Release
 | 
				
			||||||
							
								
								
									
										1
									
								
								run-acceptance-tests.ps1
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								run-acceptance-tests.ps1
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					./build -target RunAcceptanceTests
 | 
				
			||||||
@@ -1,15 +0,0 @@
 | 
				
			|||||||
echo -------------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
echo Running Ocelot.Benchmarks
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
cd test/Ocelot.Benchmarks
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
dotnet restore 
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
dotnet run 
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
cd ../../
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							
								
								
									
										1
									
								
								run-benchmarks.ps1
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								run-benchmarks.ps1
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					./build.ps1 -target RunBenchmarkTests
 | 
				
			||||||
@@ -1,17 +0,0 @@
 | 
				
			|||||||
echo -------------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
echo Restoring Ocelot
 | 
					 | 
				
			||||||
dotnet restore src/Ocelot
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
echo Restoring Ocelot.ManualTest
 | 
					 | 
				
			||||||
dotnet restore test/Ocelot.ManualTest/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
echo Running Ocelot.UnitTests
 | 
					 | 
				
			||||||
dotnet restore test/Ocelot.UnitTests/
 | 
					 | 
				
			||||||
dotnet test test/Ocelot.UnitTests/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
echo Running Ocelot.AcceptanceTests
 | 
					 | 
				
			||||||
cd test/Ocelot.AcceptanceTests/
 | 
					 | 
				
			||||||
dotnet restore 
 | 
					 | 
				
			||||||
dotnet test 
 | 
					 | 
				
			||||||
cd ../../
 | 
					 | 
				
			||||||
							
								
								
									
										1
									
								
								run-unit-tests.ps1
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								run-unit-tests.ps1
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					./build.ps1 -target RunUnitTests
 | 
				
			||||||
@@ -12,18 +12,18 @@ namespace Ocelot.Authentication.Handler.Creator
 | 
				
			|||||||
    /// </summary>
 | 
					    /// </summary>
 | 
				
			||||||
    public class AuthenticationHandlerCreator : IAuthenticationHandlerCreator
 | 
					    public class AuthenticationHandlerCreator : IAuthenticationHandlerCreator
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public Response<RequestDelegate> CreateIdentityServerAuthenticationHandler(IApplicationBuilder app, AuthenticationOptions authOptions)
 | 
					        public Response<RequestDelegate> Create(IApplicationBuilder app, AuthenticationOptions authOptions)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var builder = app.New();
 | 
					            var builder = app.New();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            builder.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
 | 
					            builder.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Authority = authOptions.ProviderRootUrl,
 | 
					                Authority = authOptions.ProviderRootUrl,
 | 
				
			||||||
                ScopeName = authOptions.ScopeName,
 | 
					                ApiName = authOptions.ScopeName,
 | 
				
			||||||
                RequireHttpsMetadata = authOptions.RequireHttps,
 | 
					                RequireHttpsMetadata = authOptions.RequireHttps,
 | 
				
			||||||
                AdditionalScopes = authOptions.AdditionalScopes,
 | 
					                AllowedScopes = authOptions.AdditionalScopes,
 | 
				
			||||||
                SupportedTokens = SupportedTokens.Both,
 | 
					                SupportedTokens = SupportedTokens.Both,
 | 
				
			||||||
                ScopeSecret = authOptions.ScopeSecret
 | 
					                ApiSecret = authOptions.ScopeSecret
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var authenticationNext = builder.Build();
 | 
					            var authenticationNext = builder.Build();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,6 @@ namespace Ocelot.Authentication.Handler.Creator
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public interface IAuthenticationHandlerCreator
 | 
					    public interface IAuthenticationHandlerCreator
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Response<RequestDelegate> CreateIdentityServerAuthenticationHandler(IApplicationBuilder app, AuthenticationOptions authOptions);
 | 
					        Response<RequestDelegate> Create(IApplicationBuilder app, AuthenticationOptions authOptions);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,7 @@ namespace Ocelot.Authentication.Handler.Factory
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public Response<AuthenticationHandler> Get(IApplicationBuilder app, AuthenticationOptions authOptions)
 | 
					        public Response<AuthenticationHandler> Get(IApplicationBuilder app, AuthenticationOptions authOptions)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var handler = _creator.CreateIdentityServerAuthenticationHandler(app, authOptions);
 | 
					            var handler = _creator.Create(app, authOptions);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!handler.IsError)
 | 
					            if (!handler.IsError)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,6 @@
 | 
				
			|||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Microsoft.AspNetCore.Builder;
 | 
					using Microsoft.AspNetCore.Builder;
 | 
				
			||||||
using Microsoft.AspNetCore.Http;
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
using Microsoft.Extensions.Logging;
 | 
					 | 
				
			||||||
using Ocelot.Authentication.Handler.Factory;
 | 
					using Ocelot.Authentication.Handler.Factory;
 | 
				
			||||||
using Ocelot.Configuration;
 | 
					using Ocelot.Configuration;
 | 
				
			||||||
using Ocelot.Errors;
 | 
					using Ocelot.Errors;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,4 @@
 | 
				
			|||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.Linq;
 | 
					 | 
				
			||||||
using System.Security.Claims;
 | 
					using System.Security.Claims;
 | 
				
			||||||
using Ocelot.Errors;
 | 
					using Ocelot.Errors;
 | 
				
			||||||
using Ocelot.Responses;
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,4 @@
 | 
				
			|||||||
using Microsoft.Extensions.Logging;
 | 
					using Ocelot.Infrastructure.RequestData;
 | 
				
			||||||
using Ocelot.Infrastructure.RequestData;
 | 
					 | 
				
			||||||
using Ocelot.Logging;
 | 
					using Ocelot.Logging;
 | 
				
			||||||
using Ocelot.Responses;
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -61,7 +60,7 @@ namespace Ocelot.Authorisation.Middleware
 | 
				
			|||||||
                    SetPipelineError(new List<Error>
 | 
					                    SetPipelineError(new List<Error>
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new UnauthorisedError(
 | 
					                        new UnauthorisedError(
 | 
				
			||||||
                            $"{context.User.Identity.Name} unable to access {DownstreamRoute.ReRoute.UpstreamTemplate}")
 | 
					                            $"{context.User.Identity.Name} unable to access {DownstreamRoute.ReRoute.UpstreamPathTemplate.Value}")
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,6 @@
 | 
				
			|||||||
using System.Net.Http;
 | 
					using System.Net.Http;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Microsoft.AspNetCore.Http;
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
using Microsoft.Extensions.Logging;
 | 
					 | 
				
			||||||
using Ocelot.Infrastructure.RequestData;
 | 
					using Ocelot.Infrastructure.RequestData;
 | 
				
			||||||
using Ocelot.Logging;
 | 
					using Ocelot.Logging;
 | 
				
			||||||
using Ocelot.Middleware;
 | 
					using Ocelot.Middleware;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,6 @@
 | 
				
			|||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Microsoft.AspNetCore.Http;
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
using Microsoft.Extensions.Logging;
 | 
					 | 
				
			||||||
using Ocelot.Infrastructure.RequestData;
 | 
					using Ocelot.Infrastructure.RequestData;
 | 
				
			||||||
using Ocelot.Logging;
 | 
					using Ocelot.Logging;
 | 
				
			||||||
using Ocelot.Middleware;
 | 
					using Ocelot.Middleware;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										22
									
								
								src/Ocelot/Configuration/Authentication/HashMatcher.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/Ocelot/Configuration/Authentication/HashMatcher.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Cryptography.KeyDerivation;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Authentication
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class HashMatcher : IHashMatcher
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public bool Match(string password, string salt, string hash)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            byte[] s = Convert.FromBase64String(salt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            string hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
 | 
				
			||||||
 | 
					                password: password,
 | 
				
			||||||
 | 
					                salt: s,
 | 
				
			||||||
 | 
					                prf: KeyDerivationPrf.HMACSHA256,
 | 
				
			||||||
 | 
					                iterationCount: 10000,
 | 
				
			||||||
 | 
					                numBytesRequested: 256 / 8));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return hashed == hash;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										7
									
								
								src/Ocelot/Configuration/Authentication/IHashMatcher.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/Ocelot/Configuration/Authentication/IHashMatcher.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					namespace Ocelot.Configuration.Authentication
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public interface IHashMatcher
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        bool Match(string password, string salt, string hash);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using IdentityServer4.Models;
 | 
				
			||||||
 | 
					using IdentityServer4.Validation;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Provider;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Authentication
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class OcelotResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private readonly IHashMatcher _matcher;
 | 
				
			||||||
 | 
					        private readonly IIdentityServerConfiguration _identityServerConfiguration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public OcelotResourceOwnerPasswordValidator(IHashMatcher matcher, IIdentityServerConfiguration identityServerConfiguration)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _identityServerConfiguration = identityServerConfiguration;
 | 
				
			||||||
 | 
					            _matcher = matcher;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            try
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var user = _identityServerConfiguration.Users.FirstOrDefault(u => u.UserName == context.UserName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if(user == null)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    context.Result = new GrantValidationResult(
 | 
				
			||||||
 | 
					                            TokenRequestErrors.InvalidGrant,
 | 
				
			||||||
 | 
					                            "invalid custom credential");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else if(_matcher.Match(context.Password, user.Salt, user.Hash))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    context.Result = new GrantValidationResult(
 | 
				
			||||||
 | 
					                        subject: "admin",
 | 
				
			||||||
 | 
					                        authenticationMethod: "custom");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    context.Result = new GrantValidationResult(
 | 
				
			||||||
 | 
					                        TokenRequestErrors.InvalidGrant,
 | 
				
			||||||
 | 
					                        "invalid custom credential");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch(Exception ex)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Console.WriteLine(ex);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,56 @@
 | 
				
			|||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Builder
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class AuthenticationOptionsBuilder
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private string _provider;
 | 
				
			||||||
 | 
					        private string _providerRootUrl;
 | 
				
			||||||
 | 
					        private string _scopeName;
 | 
				
			||||||
 | 
					        private string _scopeSecret;
 | 
				
			||||||
 | 
					        private bool _requireHttps;
 | 
				
			||||||
 | 
					        private List<string> _additionalScopes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public AuthenticationOptionsBuilder WithProvider(string provider)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _provider = provider;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public AuthenticationOptionsBuilder WithProviderRootUrl(string providerRootUrl)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _providerRootUrl = providerRootUrl;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public AuthenticationOptionsBuilder WithScopeName(string scopeName)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _scopeName = scopeName;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public AuthenticationOptionsBuilder WithScopeSecret(string scopeSecret)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _scopeSecret = scopeSecret;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public AuthenticationOptionsBuilder WithRequireHttps(bool requireHttps)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _requireHttps = requireHttps;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public AuthenticationOptionsBuilder WithAdditionalScopes(List<string> additionalScopes)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _additionalScopes = additionalScopes;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public AuthenticationOptions Build()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return new AuthenticationOptions(_provider, _providerRootUrl, _scopeName, _requireHttps, _additionalScopes, _scopeSecret);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										34
									
								
								src/Ocelot/Configuration/Builder/QoSOptionsBuilder.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/Ocelot/Configuration/Builder/QoSOptionsBuilder.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					namespace Ocelot.Configuration.Builder
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class QoSOptionsBuilder
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private int _exceptionsAllowedBeforeBreaking;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private int _durationOfBreak;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private int _timeoutValue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public QoSOptionsBuilder WithExceptionsAllowedBeforeBreaking(int exceptionsAllowedBeforeBreaking)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _exceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public QoSOptionsBuilder WithDurationOfBreak(int durationOfBreak)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _durationOfBreak = durationOfBreak;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public QoSOptionsBuilder WithTimeoutValue(int timeoutValue)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _timeoutValue = timeoutValue;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public QoSOptions Build()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return new QoSOptions(_exceptionsAllowedBeforeBreaking, _durationOfBreak, _timeoutValue);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,20 +1,18 @@
 | 
				
			|||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Net.Http;
 | 
				
			||||||
 | 
					using Ocelot.Values;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.Configuration.Builder
 | 
					namespace Ocelot.Configuration.Builder
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class ReRouteBuilder
 | 
					    public class ReRouteBuilder
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private string _downstreamTemplate;
 | 
					        private AuthenticationOptions _authenticationOptions;
 | 
				
			||||||
 | 
					        private string _loadBalancerKey;
 | 
				
			||||||
 | 
					        private string _downstreamPathTemplate;
 | 
				
			||||||
        private string _upstreamTemplate;
 | 
					        private string _upstreamTemplate;
 | 
				
			||||||
        private string _upstreamTemplatePattern;
 | 
					        private string _upstreamTemplatePattern;
 | 
				
			||||||
        private string _upstreamHttpMethod;
 | 
					        private string _upstreamHttpMethod;
 | 
				
			||||||
        private bool _isAuthenticated;
 | 
					        private bool _isAuthenticated;
 | 
				
			||||||
        private string _authenticationProvider;
 | 
					 | 
				
			||||||
        private string _authenticationProviderUrl;
 | 
					 | 
				
			||||||
        private string _scopeName;
 | 
					 | 
				
			||||||
        private List<string> _additionalScopes;
 | 
					 | 
				
			||||||
        private bool _requireHttps;
 | 
					 | 
				
			||||||
        private string _scopeSecret;
 | 
					 | 
				
			||||||
        private List<ClaimToThing> _configHeaderExtractorProperties;
 | 
					        private List<ClaimToThing> _configHeaderExtractorProperties;
 | 
				
			||||||
        private List<ClaimToThing> _claimToClaims;
 | 
					        private List<ClaimToThing> _claimToClaims;
 | 
				
			||||||
        private Dictionary<string, string> _routeClaimRequirement;
 | 
					        private Dictionary<string, string> _routeClaimRequirement;
 | 
				
			||||||
@@ -23,19 +21,41 @@ namespace Ocelot.Configuration.Builder
 | 
				
			|||||||
        private string _requestIdHeaderKey;
 | 
					        private string _requestIdHeaderKey;
 | 
				
			||||||
        private bool _isCached;
 | 
					        private bool _isCached;
 | 
				
			||||||
        private CacheOptions _fileCacheOptions;
 | 
					        private CacheOptions _fileCacheOptions;
 | 
				
			||||||
 | 
					        private string _downstreamScheme;
 | 
				
			||||||
 | 
					        private string _downstreamHost;
 | 
				
			||||||
 | 
					        private int _downstreamPort;
 | 
				
			||||||
 | 
					        private string _loadBalancer;
 | 
				
			||||||
 | 
					        private ServiceProviderConfiguraion _serviceProviderConfiguraion;
 | 
				
			||||||
 | 
					        private bool _useQos;
 | 
				
			||||||
 | 
					        private QoSOptions _qosOptions;
 | 
				
			||||||
 | 
					        public bool _enableRateLimiting;
 | 
				
			||||||
 | 
					        public RateLimitOptions _rateLimitOptions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ReRouteBuilder()
 | 
					        public ReRouteBuilder WithLoadBalancer(string loadBalancer)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _additionalScopes = new List<string>();
 | 
					          _loadBalancer = loadBalancer;
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public ReRouteBuilder WithDownstreamTemplate(string input)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _downstreamTemplate = input;
 | 
					 | 
				
			||||||
            return this;
 | 
					            return this;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ReRouteBuilder WithUpstreamTemplate(string input)
 | 
					        public ReRouteBuilder WithDownstreamScheme(string downstreamScheme)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _downstreamScheme = downstreamScheme;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ReRouteBuilder WithDownstreamHost(string downstreamHost)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _downstreamHost = downstreamHost;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ReRouteBuilder WithDownstreamPathTemplate(string input)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _downstreamPathTemplate = input;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ReRouteBuilder WithUpstreamPathTemplate(string input)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _upstreamTemplate = input;
 | 
					            _upstreamTemplate = input;
 | 
				
			||||||
            return this;
 | 
					            return this;
 | 
				
			||||||
@@ -63,42 +83,6 @@ namespace Ocelot.Configuration.Builder
 | 
				
			|||||||
            return this;
 | 
					            return this;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ReRouteBuilder WithAuthenticationProvider(string input)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _authenticationProvider = input;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public ReRouteBuilder WithAuthenticationProviderUrl(string input)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _authenticationProviderUrl = input;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public ReRouteBuilder WithAuthenticationProviderScopeName(string input)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _scopeName = input;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public ReRouteBuilder WithAuthenticationProviderAdditionalScopes(List<string> input)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _additionalScopes = input;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public ReRouteBuilder WithRequireHttps(bool input)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _requireHttps = input;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public ReRouteBuilder WithScopeSecret(string input)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _scopeSecret = input;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public ReRouteBuilder WithRequestIdKey(string input)
 | 
					        public ReRouteBuilder WithRequestIdKey(string input)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _requestIdHeaderKey = input;
 | 
					            _requestIdHeaderKey = input;
 | 
				
			||||||
@@ -141,12 +125,83 @@ namespace Ocelot.Configuration.Builder
 | 
				
			|||||||
            return this;
 | 
					            return this;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ReRouteBuilder WithDownstreamPort(int port)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _downstreamPort = port;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ReRouteBuilder WithIsQos(bool input)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _useQos = input;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ReRouteBuilder WithQosOptions(QoSOptions input)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _qosOptions = input;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					       
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ReRouteBuilder WithLoadBalancerKey(string loadBalancerKey)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _loadBalancerKey = loadBalancerKey;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ReRouteBuilder WithServiceProviderConfiguraion(ServiceProviderConfiguraion serviceProviderConfiguraion)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _serviceProviderConfiguraion = serviceProviderConfiguraion;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ReRouteBuilder WithAuthenticationOptions(AuthenticationOptions authenticationOptions)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _authenticationOptions = authenticationOptions;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ReRouteBuilder WithEnableRateLimiting(bool input)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _enableRateLimiting = input;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ReRouteBuilder WithRateLimitOptions(RateLimitOptions input)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _rateLimitOptions = input;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ReRoute Build()
 | 
					        public ReRoute Build()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return new ReRoute(_downstreamTemplate, _upstreamTemplate, _upstreamHttpMethod, _upstreamTemplatePattern, 
 | 
					            return new ReRoute(
 | 
				
			||||||
                _isAuthenticated, new AuthenticationOptions(_authenticationProvider, _authenticationProviderUrl, _scopeName, 
 | 
					                new PathTemplate(_downstreamPathTemplate), 
 | 
				
			||||||
                _requireHttps, _additionalScopes, _scopeSecret), _configHeaderExtractorProperties, _claimToClaims, _routeClaimRequirement, 
 | 
					                new PathTemplate(_upstreamTemplate), 
 | 
				
			||||||
                _isAuthorised, _claimToQueries, _requestIdHeaderKey, _isCached, _fileCacheOptions);
 | 
					                new HttpMethod(_upstreamHttpMethod), 
 | 
				
			||||||
 | 
					                _upstreamTemplatePattern, 
 | 
				
			||||||
 | 
					                _isAuthenticated, 
 | 
				
			||||||
 | 
					                _authenticationOptions,
 | 
				
			||||||
 | 
					                _configHeaderExtractorProperties, 
 | 
				
			||||||
 | 
					                _claimToClaims, 
 | 
				
			||||||
 | 
					                _routeClaimRequirement, 
 | 
				
			||||||
 | 
					                _isAuthorised, 
 | 
				
			||||||
 | 
					                _claimToQueries, 
 | 
				
			||||||
 | 
					                _requestIdHeaderKey, 
 | 
				
			||||||
 | 
					                _isCached, 
 | 
				
			||||||
 | 
					                _fileCacheOptions, 
 | 
				
			||||||
 | 
					                _downstreamScheme, 
 | 
				
			||||||
 | 
					                _loadBalancer,
 | 
				
			||||||
 | 
					                _downstreamHost, 
 | 
				
			||||||
 | 
					                _downstreamPort, 
 | 
				
			||||||
 | 
					                _loadBalancerKey, 
 | 
				
			||||||
 | 
					                _serviceProviderConfiguraion, 
 | 
				
			||||||
 | 
					                _useQos, 
 | 
				
			||||||
 | 
					                _qosOptions,
 | 
				
			||||||
 | 
					                _enableRateLimiting,
 | 
				
			||||||
 | 
					                _rateLimitOptions);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,62 @@
 | 
				
			|||||||
 | 
					namespace Ocelot.Configuration.Builder
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class ServiceProviderConfiguraionBuilder
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private string _serviceName;
 | 
				
			||||||
 | 
					        private string _downstreamHost;
 | 
				
			||||||
 | 
					        private int _downstreamPort;
 | 
				
			||||||
 | 
					        private bool _userServiceDiscovery;
 | 
				
			||||||
 | 
					        private string _serviceDiscoveryProvider;
 | 
				
			||||||
 | 
					        private string _serviceDiscoveryProviderHost;
 | 
				
			||||||
 | 
					        private int _serviceDiscoveryProviderPort;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ServiceProviderConfiguraionBuilder WithServiceName(string serviceName)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _serviceName = serviceName;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ServiceProviderConfiguraionBuilder WithDownstreamHost(string downstreamHost)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _downstreamHost = downstreamHost;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ServiceProviderConfiguraionBuilder WithDownstreamPort(int downstreamPort)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _downstreamPort = downstreamPort;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ServiceProviderConfiguraionBuilder WithUseServiceDiscovery(bool userServiceDiscovery)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _userServiceDiscovery = userServiceDiscovery;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ServiceProviderConfiguraionBuilder WithServiceDiscoveryProvider(string serviceDiscoveryProvider)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _serviceDiscoveryProvider = serviceDiscoveryProvider;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ServiceProviderConfiguraionBuilder WithServiceDiscoveryProviderHost(string serviceDiscoveryProviderHost)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _serviceDiscoveryProviderHost = serviceDiscoveryProviderHost;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ServiceProviderConfiguraionBuilder WithServiceDiscoveryProviderPort(int serviceDiscoveryProviderPort)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _serviceDiscoveryProviderPort = serviceDiscoveryProviderPort;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public ServiceProviderConfiguraion Build()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return new ServiceProviderConfiguraion(_serviceName, _downstreamHost, _downstreamPort, _userServiceDiscovery,
 | 
				
			||||||
 | 
					            _serviceDiscoveryProvider, _serviceDiscoveryProviderHost,_serviceDiscoveryProviderPort);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,11 +1,15 @@
 | 
				
			|||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.Text;
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Microsoft.Extensions.Logging;
 | 
					using Microsoft.Extensions.Logging;
 | 
				
			||||||
using Microsoft.Extensions.Options;
 | 
					using Microsoft.Extensions.Options;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Builder;
 | 
				
			||||||
using Ocelot.Configuration.File;
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
using Ocelot.Configuration.Parser;
 | 
					using Ocelot.Configuration.Parser;
 | 
				
			||||||
using Ocelot.Configuration.Validator;
 | 
					using Ocelot.Configuration.Validator;
 | 
				
			||||||
 | 
					using Ocelot.LoadBalancer.LoadBalancers;
 | 
				
			||||||
 | 
					using Ocelot.Requester.QoS;
 | 
				
			||||||
using Ocelot.Responses;
 | 
					using Ocelot.Responses;
 | 
				
			||||||
using Ocelot.Utilities;
 | 
					using Ocelot.Utilities;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -21,36 +25,52 @@ namespace Ocelot.Configuration.Creator
 | 
				
			|||||||
        private const string RegExMatchEverything = ".*";
 | 
					        private const string RegExMatchEverything = ".*";
 | 
				
			||||||
        private const string RegExMatchEndString = "$";
 | 
					        private const string RegExMatchEndString = "$";
 | 
				
			||||||
        private const string RegExIgnoreCase = "(?i)";
 | 
					        private const string RegExIgnoreCase = "(?i)";
 | 
				
			||||||
 | 
					        private const string RegExForwardSlashOnly = "^/$";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private readonly IClaimToThingConfigurationParser _claimToThingConfigurationParser;
 | 
					        private readonly IClaimToThingConfigurationParser _claimToThingConfigurationParser;
 | 
				
			||||||
        private readonly ILogger<FileOcelotConfigurationCreator> _logger;
 | 
					        private readonly ILogger<FileOcelotConfigurationCreator> _logger;
 | 
				
			||||||
 | 
					        private readonly ILoadBalancerFactory _loadBalanceFactory;
 | 
				
			||||||
 | 
					        private readonly ILoadBalancerHouse _loadBalancerHouse;
 | 
				
			||||||
 | 
					        private readonly IQoSProviderFactory _qoSProviderFactory;
 | 
				
			||||||
 | 
					        private readonly IQosProviderHouse _qosProviderHouse;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public FileOcelotConfigurationCreator(
 | 
					        public FileOcelotConfigurationCreator(
 | 
				
			||||||
            IOptions<FileConfiguration> options, 
 | 
					            IOptions<FileConfiguration> options, 
 | 
				
			||||||
            IConfigurationValidator configurationValidator, 
 | 
					            IConfigurationValidator configurationValidator, 
 | 
				
			||||||
            IClaimToThingConfigurationParser claimToThingConfigurationParser, 
 | 
					            IClaimToThingConfigurationParser claimToThingConfigurationParser, 
 | 
				
			||||||
            ILogger<FileOcelotConfigurationCreator> logger)
 | 
					            ILogger<FileOcelotConfigurationCreator> logger,
 | 
				
			||||||
 | 
					            ILoadBalancerFactory loadBalancerFactory,
 | 
				
			||||||
 | 
					            ILoadBalancerHouse loadBalancerHouse, 
 | 
				
			||||||
 | 
					            IQoSProviderFactory qoSProviderFactory, 
 | 
				
			||||||
 | 
					            IQosProviderHouse qosProviderHouse)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            _loadBalanceFactory = loadBalancerFactory;
 | 
				
			||||||
 | 
					            _loadBalancerHouse = loadBalancerHouse;
 | 
				
			||||||
 | 
					            _qoSProviderFactory = qoSProviderFactory;
 | 
				
			||||||
 | 
					            _qosProviderHouse = qosProviderHouse;
 | 
				
			||||||
            _options = options;
 | 
					            _options = options;
 | 
				
			||||||
            _configurationValidator = configurationValidator;
 | 
					            _configurationValidator = configurationValidator;
 | 
				
			||||||
            _claimToThingConfigurationParser = claimToThingConfigurationParser;
 | 
					            _claimToThingConfigurationParser = claimToThingConfigurationParser;
 | 
				
			||||||
            _logger = logger;
 | 
					            _logger = logger;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Response<IOcelotConfiguration> Create()
 | 
					        public async Task<Response<IOcelotConfiguration>> Create()
 | 
				
			||||||
        {     
 | 
					        {     
 | 
				
			||||||
            var config = SetUpConfiguration();
 | 
					            var config = await SetUpConfiguration(_options.Value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return new OkResponse<IOcelotConfiguration>(config);
 | 
					            return new OkResponse<IOcelotConfiguration>(config);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        public async Task<Response<IOcelotConfiguration>> Create(FileConfiguration fileConfiguration)
 | 
				
			||||||
        /// This method is meant to be tempoary to convert a config to an ocelot config...probably wont keep this but we will see
 | 
					 | 
				
			||||||
        /// will need a refactor at some point as its crap
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        private IOcelotConfiguration SetUpConfiguration()
 | 
					 | 
				
			||||||
        {     
 | 
					        {     
 | 
				
			||||||
            var response = _configurationValidator.IsValid(_options.Value);
 | 
					            var config = await SetUpConfiguration(fileConfiguration);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return new OkResponse<IOcelotConfiguration>(config);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private async Task<IOcelotConfiguration> SetUpConfiguration(FileConfiguration fileConfiguration)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var response = _configurationValidator.IsValid(fileConfiguration);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (response.Data.IsError)
 | 
					            if (response.Data.IsError)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -66,59 +86,190 @@ namespace Ocelot.Configuration.Creator
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            var reRoutes = new List<ReRoute>();
 | 
					            var reRoutes = new List<ReRoute>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            foreach (var reRoute in _options.Value.ReRoutes)
 | 
					            foreach (var reRoute in fileConfiguration.ReRoutes)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var ocelotReRoute = SetUpReRoute(reRoute, _options.Value.GlobalConfiguration);
 | 
					                var ocelotReRoute = await SetUpReRoute(reRoute, fileConfiguration.GlobalConfiguration);
 | 
				
			||||||
                reRoutes.Add(ocelotReRoute);
 | 
					                reRoutes.Add(ocelotReRoute);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            return new OcelotConfiguration(reRoutes);
 | 
					            return new OcelotConfiguration(reRoutes, fileConfiguration.GlobalConfiguration.AdministrationPath);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private ReRoute SetUpReRoute(FileReRoute reRoute, FileGlobalConfiguration globalConfiguration)
 | 
					        private async Task<ReRoute> SetUpReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var isAuthenticated = IsAuthenticated(fileReRoute);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var isAuthorised = IsAuthorised(fileReRoute);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var isCached = IsCached(fileReRoute);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var requestIdKey = BuildRequestId(fileReRoute, globalConfiguration);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var reRouteKey = BuildReRouteKey(fileReRoute);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var upstreamTemplatePattern = BuildUpstreamTemplatePattern(fileReRoute);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var isQos = IsQoS(fileReRoute);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var serviceProviderConfiguration = BuildServiceProviderConfiguration(fileReRoute, globalConfiguration);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var authOptionsForRoute = BuildAuthenticationOptions(fileReRoute);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var claimsToHeaders = BuildAddThingsToRequest(fileReRoute.AddHeadersToRequest);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var claimsToClaims = BuildAddThingsToRequest(fileReRoute.AddClaimsToRequest);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var claimsToQueries = BuildAddThingsToRequest(fileReRoute.AddQueriesToRequest);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var qosOptions = BuildQoSOptions(fileReRoute);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var enableRateLimiting = IsEnableRateLimiting(fileReRoute);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var rateLimitOption = BuildRateLimitOptions(fileReRoute, globalConfiguration, enableRateLimiting);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var reRoute = new ReRouteBuilder()
 | 
				
			||||||
 | 
					                .WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate)
 | 
				
			||||||
 | 
					                .WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate)
 | 
				
			||||||
 | 
					                .WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod)
 | 
				
			||||||
 | 
					                .WithUpstreamTemplatePattern(upstreamTemplatePattern)
 | 
				
			||||||
 | 
					                .WithIsAuthenticated(isAuthenticated)
 | 
				
			||||||
 | 
					                .WithAuthenticationOptions(authOptionsForRoute)
 | 
				
			||||||
 | 
					                .WithClaimsToHeaders(claimsToHeaders)
 | 
				
			||||||
 | 
					                .WithClaimsToClaims(claimsToClaims)
 | 
				
			||||||
 | 
					                .WithRouteClaimsRequirement(fileReRoute.RouteClaimsRequirement)
 | 
				
			||||||
 | 
					                .WithIsAuthorised(isAuthorised)
 | 
				
			||||||
 | 
					                .WithClaimsToQueries(claimsToQueries)
 | 
				
			||||||
 | 
					                .WithRequestIdKey(requestIdKey)
 | 
				
			||||||
 | 
					                .WithIsCached(isCached)
 | 
				
			||||||
 | 
					                .WithCacheOptions(new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds))
 | 
				
			||||||
 | 
					                .WithDownstreamScheme(fileReRoute.DownstreamScheme)
 | 
				
			||||||
 | 
					                .WithLoadBalancer(fileReRoute.LoadBalancer)
 | 
				
			||||||
 | 
					                .WithDownstreamHost(fileReRoute.DownstreamHost)
 | 
				
			||||||
 | 
					                .WithDownstreamPort(fileReRoute.DownstreamPort)
 | 
				
			||||||
 | 
					                .WithLoadBalancerKey(reRouteKey)
 | 
				
			||||||
 | 
					                .WithServiceProviderConfiguraion(serviceProviderConfiguration)
 | 
				
			||||||
 | 
					                .WithIsQos(isQos)
 | 
				
			||||||
 | 
					                .WithQosOptions(qosOptions)
 | 
				
			||||||
 | 
					                .WithEnableRateLimiting(enableRateLimiting)
 | 
				
			||||||
 | 
					                .WithRateLimitOptions(rateLimitOption)
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					            await SetupLoadBalancer(reRoute);
 | 
				
			||||||
 | 
					            SetupQosProvider(reRoute);
 | 
				
			||||||
 | 
					            return reRoute;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private static RateLimitOptions BuildRateLimitOptions(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration, bool enableRateLimiting)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            RateLimitOptions rateLimitOption = null;
 | 
				
			||||||
 | 
					            if (enableRateLimiting)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                rateLimitOption = new RateLimitOptions(enableRateLimiting, globalConfiguration.RateLimitOptions.ClientIdHeader,
 | 
				
			||||||
 | 
					                   fileReRoute.RateLimitOptions.ClientWhitelist, globalConfiguration.RateLimitOptions.DisableRateLimitHeaders,
 | 
				
			||||||
 | 
					                   globalConfiguration.RateLimitOptions.QuotaExceededMessage, globalConfiguration.RateLimitOptions.RateLimitCounterPrefix,
 | 
				
			||||||
 | 
					                   new RateLimitRule(fileReRoute.RateLimitOptions.Period, TimeSpan.FromSeconds(fileReRoute.RateLimitOptions.PeriodTimespan), fileReRoute.RateLimitOptions.Limit)
 | 
				
			||||||
 | 
					                   , globalConfiguration.RateLimitOptions.HttpStatusCode);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return rateLimitOption;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private static bool IsEnableRateLimiting(FileReRoute fileReRoute)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return (fileReRoute.RateLimitOptions != null && fileReRoute.RateLimitOptions.EnableRateLimiting) ? true : false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private QoSOptions BuildQoSOptions(FileReRoute fileReRoute)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return new QoSOptionsBuilder()
 | 
				
			||||||
 | 
					                .WithExceptionsAllowedBeforeBreaking(fileReRoute.QoSOptions.ExceptionsAllowedBeforeBreaking)
 | 
				
			||||||
 | 
					                .WithDurationOfBreak(fileReRoute.QoSOptions.DurationOfBreak)
 | 
				
			||||||
 | 
					                .WithTimeoutValue(fileReRoute.QoSOptions.TimeoutValue)
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private bool IsQoS(FileReRoute fileReRoute)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return fileReRoute.QoSOptions?.ExceptionsAllowedBeforeBreaking > 0 && fileReRoute.QoSOptions?.TimeoutValue > 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private bool IsAuthenticated(FileReRoute fileReRoute)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return !string.IsNullOrEmpty(fileReRoute.AuthenticationOptions?.Provider);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					        private bool IsAuthorised(FileReRoute fileReRoute)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return fileReRoute.RouteClaimsRequirement?.Count > 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private bool IsCached(FileReRoute fileReRoute)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return fileReRoute.FileCacheOptions.TtlSeconds > 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private string BuildRequestId(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var globalRequestIdConfiguration = !string.IsNullOrEmpty(globalConfiguration?.RequestIdKey);
 | 
					            var globalRequestIdConfiguration = !string.IsNullOrEmpty(globalConfiguration?.RequestIdKey);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var upstreamTemplate = BuildUpstreamTemplate(reRoute);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var isAuthenticated = !string.IsNullOrEmpty(reRoute.AuthenticationOptions?.Provider);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var isAuthorised = reRoute.RouteClaimsRequirement?.Count > 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var isCached = reRoute.FileCacheOptions.TtlSeconds > 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
             var requestIdKey = globalRequestIdConfiguration
 | 
					             var requestIdKey = globalRequestIdConfiguration
 | 
				
			||||||
                ? globalConfiguration.RequestIdKey
 | 
					                ? globalConfiguration.RequestIdKey
 | 
				
			||||||
                : reRoute.RequestIdKey;
 | 
					                : fileReRoute.RequestIdKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (isAuthenticated)
 | 
					                return requestIdKey;
 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var authOptionsForRoute = new AuthenticationOptions(reRoute.AuthenticationOptions.Provider,
 | 
					 | 
				
			||||||
                    reRoute.AuthenticationOptions.ProviderRootUrl, reRoute.AuthenticationOptions.ScopeName,
 | 
					 | 
				
			||||||
                    reRoute.AuthenticationOptions.RequireHttps, reRoute.AuthenticationOptions.AdditionalScopes,
 | 
					 | 
				
			||||||
                    reRoute.AuthenticationOptions.ScopeSecret);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                var claimsToHeaders = GetAddThingsToRequest(reRoute.AddHeadersToRequest);
 | 
					 | 
				
			||||||
                var claimsToClaims = GetAddThingsToRequest(reRoute.AddClaimsToRequest);
 | 
					 | 
				
			||||||
                var claimsToQueries = GetAddThingsToRequest(reRoute.AddQueriesToRequest);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                return new ReRoute(reRoute.DownstreamTemplate, reRoute.UpstreamTemplate,
 | 
					 | 
				
			||||||
                    reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated,
 | 
					 | 
				
			||||||
                    authOptionsForRoute, claimsToHeaders, claimsToClaims,
 | 
					 | 
				
			||||||
                    reRoute.RouteClaimsRequirement, isAuthorised, claimsToQueries,
 | 
					 | 
				
			||||||
                    requestIdKey, isCached, new CacheOptions(reRoute.FileCacheOptions.TtlSeconds));
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return new ReRoute(reRoute.DownstreamTemplate, reRoute.UpstreamTemplate, 
 | 
					        private string BuildReRouteKey(FileReRoute fileReRoute)
 | 
				
			||||||
                reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated, 
 | 
					        {
 | 
				
			||||||
                null, new List<ClaimToThing>(), new List<ClaimToThing>(), 
 | 
					            //note - not sure if this is the correct key, but this is probably the only unique key i can think of given my poor brain
 | 
				
			||||||
                reRoute.RouteClaimsRequirement, isAuthorised, new List<ClaimToThing>(),
 | 
					            var loadBalancerKey = $"{fileReRoute.UpstreamPathTemplate}{fileReRoute.UpstreamHttpMethod}";
 | 
				
			||||||
                    requestIdKey, isCached, new CacheOptions(reRoute.FileCacheOptions.TtlSeconds));
 | 
					            return loadBalancerKey;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private string BuildUpstreamTemplate(FileReRoute reRoute)
 | 
					        private AuthenticationOptions BuildAuthenticationOptions(FileReRoute fileReRoute)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var upstreamTemplate = reRoute.UpstreamTemplate;
 | 
					            return new AuthenticationOptionsBuilder()
 | 
				
			||||||
 | 
					                                        .WithProvider(fileReRoute.AuthenticationOptions?.Provider)
 | 
				
			||||||
 | 
					                                        .WithProviderRootUrl(fileReRoute.AuthenticationOptions?.ProviderRootUrl)
 | 
				
			||||||
 | 
					                                        .WithScopeName(fileReRoute.AuthenticationOptions?.ScopeName)
 | 
				
			||||||
 | 
					                                        .WithRequireHttps(fileReRoute.AuthenticationOptions.RequireHttps)
 | 
				
			||||||
 | 
					                                        .WithAdditionalScopes(fileReRoute.AuthenticationOptions?.AdditionalScopes)
 | 
				
			||||||
 | 
					                                        .WithScopeSecret(fileReRoute.AuthenticationOptions?.ScopeSecret)
 | 
				
			||||||
 | 
					                                        .Build();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private async Task SetupLoadBalancer(ReRoute reRoute)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var loadBalancer = await _loadBalanceFactory.Get(reRoute);
 | 
				
			||||||
 | 
					            _loadBalancerHouse.Add(reRoute.ReRouteKey, loadBalancer);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void SetupQosProvider(ReRoute reRoute)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var loadBalancer = _qoSProviderFactory.Get(reRoute);
 | 
				
			||||||
 | 
					            _qosProviderHouse.Add(reRoute.ReRouteKey, loadBalancer);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private ServiceProviderConfiguraion BuildServiceProviderConfiguration(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var useServiceDiscovery = !string.IsNullOrEmpty(fileReRoute.ServiceName)
 | 
				
			||||||
 | 
					                && !string.IsNullOrEmpty(globalConfiguration?.ServiceDiscoveryProvider?.Provider);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var serviceProviderPort = globalConfiguration?.ServiceDiscoveryProvider?.Port ?? 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return new ServiceProviderConfiguraionBuilder()
 | 
				
			||||||
 | 
					                    .WithServiceName(fileReRoute.ServiceName)
 | 
				
			||||||
 | 
					                    .WithDownstreamHost(fileReRoute.DownstreamHost)
 | 
				
			||||||
 | 
					                    .WithDownstreamPort(fileReRoute.DownstreamPort)
 | 
				
			||||||
 | 
					                    .WithUseServiceDiscovery(useServiceDiscovery)
 | 
				
			||||||
 | 
					                    .WithServiceDiscoveryProvider(globalConfiguration?.ServiceDiscoveryProvider?.Provider)
 | 
				
			||||||
 | 
					                    .WithServiceDiscoveryProviderHost(globalConfiguration?.ServiceDiscoveryProvider?.Host)
 | 
				
			||||||
 | 
					                    .WithServiceDiscoveryProviderPort(serviceProviderPort)
 | 
				
			||||||
 | 
					                    .Build();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private string BuildUpstreamTemplatePattern(FileReRoute reRoute)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var upstreamTemplate = reRoute.UpstreamPathTemplate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            upstreamTemplate = upstreamTemplate.SetLastCharacterAs('/');
 | 
					            upstreamTemplate = upstreamTemplate.SetLastCharacterAs('/');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -140,6 +291,11 @@ namespace Ocelot.Configuration.Creator
 | 
				
			|||||||
                upstreamTemplate = upstreamTemplate.Replace(placeholder, RegExMatchEverything);
 | 
					                upstreamTemplate = upstreamTemplate.Replace(placeholder, RegExMatchEverything);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (upstreamTemplate == "/")
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return RegExForwardSlashOnly;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var route = reRoute.ReRouteIsCaseSensitive 
 | 
					            var route = reRoute.ReRouteIsCaseSensitive 
 | 
				
			||||||
                ? $"{upstreamTemplate}{RegExMatchEndString}" 
 | 
					                ? $"{upstreamTemplate}{RegExMatchEndString}" 
 | 
				
			||||||
                : $"{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}";
 | 
					                : $"{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}";
 | 
				
			||||||
@@ -147,7 +303,7 @@ namespace Ocelot.Configuration.Creator
 | 
				
			|||||||
            return route;
 | 
					            return route;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private List<ClaimToThing> GetAddThingsToRequest(Dictionary<string,string> thingBeingAdded)
 | 
					        private List<ClaimToThing> BuildAddThingsToRequest(Dictionary<string,string> thingBeingAdded)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var claimsToTHings = new List<ClaimToThing>();
 | 
					            var claimsToTHings = new List<ClaimToThing>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,12 @@
 | 
				
			|||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
using Ocelot.Responses;
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.Configuration.Creator
 | 
					namespace Ocelot.Configuration.Creator
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public interface IOcelotConfigurationCreator
 | 
					    public interface IOcelotConfigurationCreator
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Response<IOcelotConfiguration> Create();
 | 
					        Task<Response<IOcelotConfiguration>> Create();
 | 
				
			||||||
 | 
					        Task<Response<IOcelotConfiguration>> Create(FileConfiguration fileConfiguration);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using IdentityServer4.AccessTokenValidation;
 | 
				
			||||||
 | 
					using IdentityServer4.Models;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Provider;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Creator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public static class IdentityServerConfigurationCreator
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public static IdentityServerConfiguration GetIdentityServerConfiguration()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var username = Environment.GetEnvironmentVariable("OCELOT_USERNAME");
 | 
				
			||||||
 | 
					            var hash = Environment.GetEnvironmentVariable("OCELOT_HASH");
 | 
				
			||||||
 | 
					            var salt = Environment.GetEnvironmentVariable("OCELOT_SALT");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return new IdentityServerConfiguration(
 | 
				
			||||||
 | 
					                "admin",
 | 
				
			||||||
 | 
					                false,
 | 
				
			||||||
 | 
					                SupportedTokens.Both,
 | 
				
			||||||
 | 
					                "secret",
 | 
				
			||||||
 | 
					                new List<string> { "admin", "openid", "offline_access" },
 | 
				
			||||||
 | 
					                "Ocelot Administration",
 | 
				
			||||||
 | 
					                true,
 | 
				
			||||||
 | 
					                GrantTypes.ResourceOwnerPassword,
 | 
				
			||||||
 | 
					                AccessTokenType.Jwt,
 | 
				
			||||||
 | 
					                false,
 | 
				
			||||||
 | 
					                new List<User>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    new User("admin", username, hash, salt)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,7 +1,19 @@
 | 
				
			|||||||
namespace Ocelot.Configuration.File
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.File
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class FileGlobalConfiguration
 | 
					    public class FileGlobalConfiguration
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        public FileGlobalConfiguration()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ServiceDiscoveryProvider = new FileServiceDiscoveryProvider();
 | 
				
			||||||
 | 
					            RateLimitOptions = new FileRateLimitOptions();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string RequestIdKey { get; set; }
 | 
					        public string RequestIdKey { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public FileServiceDiscoveryProvider ServiceDiscoveryProvider {get;set;}
 | 
				
			||||||
 | 
					        public string AdministrationPath {get;set;}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public FileRateLimitOptions RateLimitOptions { get; set; }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.File
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class FileGlobalConfiguration
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public FileGlobalConfiguration()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ServiceDiscoveryProvider = new FileServiceDiscoveryProvider();
 | 
				
			||||||
 | 
					            RateLimitOptions = new FileRateLimitOptions();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string RequestIdKey { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public FileServiceDiscoveryProvider ServiceDiscoveryProvider {get;set;}
 | 
				
			||||||
 | 
					<<<<<<< HEAD
 | 
				
			||||||
 | 
					        public string AdministrationPath {get;set;}
 | 
				
			||||||
 | 
					=======
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public FileRateLimitOptions RateLimitOptions { get; set; }
 | 
				
			||||||
 | 
					>>>>>>> develop
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										11
									
								
								src/Ocelot/Configuration/File/FileQoSOptions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/Ocelot/Configuration/File/FileQoSOptions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					namespace Ocelot.Configuration.File
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class FileQoSOptions
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public int ExceptionsAllowedBeforeBreaking { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public int DurationOfBreak { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public int TimeoutValue { get; set; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										39
									
								
								src/Ocelot/Configuration/File/FileRateLimitOptions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/Ocelot/Configuration/File/FileRateLimitOptions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.File
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class FileRateLimitOptions
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Gets or sets the HTTP header that holds the client identifier, by default is X-ClientId
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public string ClientIdHeader { get; set; } = "ClientId";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Gets or sets a value that will be used as a formatter for the QuotaExceeded response message.
 | 
				
			||||||
 | 
					        /// If none specified the default will be: 
 | 
				
			||||||
 | 
					        /// API calls quota exceeded! maximum admitted {0} per {1}
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public string QuotaExceededMessage { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Gets or sets the counter prefix, used to compose the rate limit counter cache key
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public string RateLimitCounterPrefix { get; set; } = "ocelot";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Disables X-Rate-Limit and Rety-After headers
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public bool DisableRateLimitHeaders { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Gets or sets the HTTP Status code returned when rate limiting occurs, by default value is set to 429 (Too Many Requests)
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public int HttpStatusCode { get;  set; } = 429;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										34
									
								
								src/Ocelot/Configuration/File/FileRateLimitRule.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/Ocelot/Configuration/File/FileRateLimitRule.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.File
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public class FileRateLimitRule
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public FileRateLimitRule()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ClientWhitelist = new List<string>();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public List<string> ClientWhitelist { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Enables endpoint rate limiting based URL path and HTTP verb
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public bool EnableRateLimiting { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Rate limit period as in 1s, 1m, 1h
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public string Period { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public double PeriodTimespan { get; set; }
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Maximum number of requests that a client can make in a defined period
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public long Limit { get; set; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -12,10 +12,12 @@ namespace Ocelot.Configuration.File
 | 
				
			|||||||
            AddQueriesToRequest = new Dictionary<string, string>();
 | 
					            AddQueriesToRequest = new Dictionary<string, string>();
 | 
				
			||||||
            AuthenticationOptions = new FileAuthenticationOptions();
 | 
					            AuthenticationOptions = new FileAuthenticationOptions();
 | 
				
			||||||
            FileCacheOptions = new FileCacheOptions();
 | 
					            FileCacheOptions = new FileCacheOptions();
 | 
				
			||||||
 | 
					            QoSOptions = new FileQoSOptions();
 | 
				
			||||||
 | 
					            RateLimitOptions = new FileRateLimitRule();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string DownstreamTemplate { get; set; }
 | 
					        public string DownstreamPathTemplate { get; set; }
 | 
				
			||||||
        public string UpstreamTemplate { get; set; }
 | 
					        public string UpstreamPathTemplate { get; set; }
 | 
				
			||||||
        public string UpstreamHttpMethod { get; set; }
 | 
					        public string UpstreamHttpMethod { get; set; }
 | 
				
			||||||
        public FileAuthenticationOptions AuthenticationOptions { get; set; }
 | 
					        public FileAuthenticationOptions AuthenticationOptions { get; set; }
 | 
				
			||||||
        public Dictionary<string, string> AddHeadersToRequest { get; set; }
 | 
					        public Dictionary<string, string> AddHeadersToRequest { get; set; }
 | 
				
			||||||
@@ -25,5 +27,12 @@ namespace Ocelot.Configuration.File
 | 
				
			|||||||
        public string RequestIdKey { get; set; }
 | 
					        public string RequestIdKey { get; set; }
 | 
				
			||||||
        public FileCacheOptions FileCacheOptions { get; set; }
 | 
					        public FileCacheOptions FileCacheOptions { get; set; }
 | 
				
			||||||
        public bool ReRouteIsCaseSensitive { get; set; }
 | 
					        public bool ReRouteIsCaseSensitive { get; set; }
 | 
				
			||||||
 | 
					        public string ServiceName { get; set; }
 | 
				
			||||||
 | 
					        public string DownstreamScheme {get;set;}
 | 
				
			||||||
 | 
					        public string DownstreamHost {get;set;}
 | 
				
			||||||
 | 
					        public int DownstreamPort { get; set; }
 | 
				
			||||||
 | 
					        public FileQoSOptions QoSOptions { get; set; }
 | 
				
			||||||
 | 
					        public string LoadBalancer {get;set;}
 | 
				
			||||||
 | 
					        public FileRateLimitRule RateLimitOptions { get; set; }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					namespace Ocelot.Configuration.File
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class FileServiceDiscoveryProvider
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string Provider {get;set;}
 | 
				
			||||||
 | 
					        public string Host {get;set;}
 | 
				
			||||||
 | 
					        public int Port { get; set; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -5,5 +5,6 @@ namespace Ocelot.Configuration
 | 
				
			|||||||
    public interface IOcelotConfiguration
 | 
					    public interface IOcelotConfiguration
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        List<ReRoute> ReRoutes { get; }
 | 
					        List<ReRoute> ReRoutes { get; }
 | 
				
			||||||
 | 
					        string AdministrationPath {get;}
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -4,11 +4,13 @@ namespace Ocelot.Configuration
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public class OcelotConfiguration : IOcelotConfiguration
 | 
					    public class OcelotConfiguration : IOcelotConfiguration
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public OcelotConfiguration(List<ReRoute> reRoutes)
 | 
					        public OcelotConfiguration(List<ReRoute> reRoutes, string administrationPath)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            ReRoutes = reRoutes;
 | 
					            ReRoutes = reRoutes;
 | 
				
			||||||
 | 
					            AdministrationPath = administrationPath;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public List<ReRoute> ReRoutes { get; }
 | 
					        public List<ReRoute> ReRoutes { get; }
 | 
				
			||||||
 | 
					        public string AdministrationPath {get;}
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.IO;
 | 
				
			||||||
 | 
					using Newtonsoft.Json;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Repository;
 | 
				
			||||||
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Provider
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class FileConfigurationProvider : IFileConfigurationProvider
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private IFileConfigurationRepository _repo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public FileConfigurationProvider(IFileConfigurationRepository repo)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _repo = repo;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public Response<FileConfiguration> Get()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var fileConfig = _repo.Get();
 | 
				
			||||||
 | 
					            return new OkResponse<FileConfiguration>(fileConfig.Data);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Provider
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public interface IFileConfigurationProvider
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Response<FileConfiguration> Get();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using IdentityServer4.AccessTokenValidation;
 | 
				
			||||||
 | 
					using IdentityServer4.Models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Provider
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public interface IIdentityServerConfiguration
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        string ApiName { get;  }
 | 
				
			||||||
 | 
					        bool RequireHttps { get;  }
 | 
				
			||||||
 | 
					        List<string> AllowedScopes { get;  }
 | 
				
			||||||
 | 
					        SupportedTokens SupportedTokens { get;  }
 | 
				
			||||||
 | 
					        string ApiSecret { get;  }
 | 
				
			||||||
 | 
					        string Description {get;}
 | 
				
			||||||
 | 
					        bool Enabled {get;}
 | 
				
			||||||
 | 
					        IEnumerable<string>  AllowedGrantTypes {get;}
 | 
				
			||||||
 | 
					        AccessTokenType AccessTokenType {get;}
 | 
				
			||||||
 | 
					        bool RequireClientSecret {get;}
 | 
				
			||||||
 | 
					        List<User> Users {get;}
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using IdentityServer4.AccessTokenValidation;
 | 
				
			||||||
 | 
					using IdentityServer4.Models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Provider
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class IdentityServerConfiguration : IIdentityServerConfiguration
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public IdentityServerConfiguration(
 | 
				
			||||||
 | 
					            string apiName, 
 | 
				
			||||||
 | 
					            bool requireHttps, 
 | 
				
			||||||
 | 
					            SupportedTokens supportedTokens, 
 | 
				
			||||||
 | 
					            string apiSecret,
 | 
				
			||||||
 | 
					            List<string> allowedScopes,
 | 
				
			||||||
 | 
					            string description,
 | 
				
			||||||
 | 
					            bool enabled,
 | 
				
			||||||
 | 
					            IEnumerable<string>  grantType,
 | 
				
			||||||
 | 
					            AccessTokenType accessTokenType,
 | 
				
			||||||
 | 
					            bool requireClientSecret,
 | 
				
			||||||
 | 
					            List<User> users)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ApiName = apiName;
 | 
				
			||||||
 | 
					            RequireHttps = requireHttps;
 | 
				
			||||||
 | 
					            SupportedTokens = supportedTokens;
 | 
				
			||||||
 | 
					            ApiSecret = apiSecret;
 | 
				
			||||||
 | 
					            AllowedScopes = allowedScopes;
 | 
				
			||||||
 | 
					            Description = description;
 | 
				
			||||||
 | 
					            Enabled = enabled;
 | 
				
			||||||
 | 
					            AllowedGrantTypes = grantType;
 | 
				
			||||||
 | 
					            AccessTokenType = accessTokenType;
 | 
				
			||||||
 | 
					            RequireClientSecret = requireClientSecret;
 | 
				
			||||||
 | 
					            Users = users;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string ApiName { get; private set; }
 | 
				
			||||||
 | 
					        public bool RequireHttps { get; private set; }
 | 
				
			||||||
 | 
					        public List<string> AllowedScopes { get; private set; }
 | 
				
			||||||
 | 
					        public SupportedTokens SupportedTokens { get; private set; }
 | 
				
			||||||
 | 
					        public string ApiSecret { get; private set; }
 | 
				
			||||||
 | 
					        public string Description {get;private set;}
 | 
				
			||||||
 | 
					        public bool Enabled {get;private set;}
 | 
				
			||||||
 | 
					        public IEnumerable<string>  AllowedGrantTypes {get;private set;}
 | 
				
			||||||
 | 
					        public AccessTokenType AccessTokenType {get;private set;}
 | 
				
			||||||
 | 
					        public bool RequireClientSecret {get;private set;}
 | 
				
			||||||
 | 
					        public List<User> Users {get;private set;}
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,4 @@
 | 
				
			|||||||
using Ocelot.Configuration.Creator;
 | 
					using Ocelot.Configuration.Repository;
 | 
				
			||||||
using Ocelot.Configuration.Repository;
 | 
					 | 
				
			||||||
using Ocelot.Responses;
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.Configuration.Provider
 | 
					namespace Ocelot.Configuration.Provider
 | 
				
			||||||
@@ -10,13 +9,10 @@ namespace Ocelot.Configuration.Provider
 | 
				
			|||||||
    public class OcelotConfigurationProvider : IOcelotConfigurationProvider
 | 
					    public class OcelotConfigurationProvider : IOcelotConfigurationProvider
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly IOcelotConfigurationRepository _repo;
 | 
					        private readonly IOcelotConfigurationRepository _repo;
 | 
				
			||||||
        private readonly IOcelotConfigurationCreator _creator;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public OcelotConfigurationProvider(IOcelotConfigurationRepository repo, 
 | 
					        public OcelotConfigurationProvider(IOcelotConfigurationRepository repo)
 | 
				
			||||||
            IOcelotConfigurationCreator creator)
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _repo = repo;
 | 
					            _repo = repo;
 | 
				
			||||||
            _creator = creator;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Response<IOcelotConfiguration> Get()
 | 
					        public Response<IOcelotConfiguration> Get()
 | 
				
			||||||
@@ -28,20 +24,6 @@ namespace Ocelot.Configuration.Provider
 | 
				
			|||||||
                return new ErrorResponse<IOcelotConfiguration>(repoConfig.Errors);
 | 
					                return new ErrorResponse<IOcelotConfiguration>(repoConfig.Errors);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (repoConfig.Data == null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var creatorConfig = _creator.Create();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (creatorConfig.IsError)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    return new ErrorResponse<IOcelotConfiguration>(creatorConfig.Errors);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                _repo.AddOrReplace(creatorConfig.Data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                return new OkResponse<IOcelotConfiguration>(creatorConfig.Data);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return new OkResponse<IOcelotConfiguration>(repoConfig.Data);
 | 
					            return new OkResponse<IOcelotConfiguration>(repoConfig.Data);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										17
									
								
								src/Ocelot/Configuration/Provider/User.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/Ocelot/Configuration/Provider/User.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					namespace Ocelot.Configuration.Provider
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class User
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public User(string subject, string userName, string hash, string salt)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Subject = subject;
 | 
				
			||||||
 | 
					            UserName = userName;
 | 
				
			||||||
 | 
					            Hash = hash;
 | 
				
			||||||
 | 
					            Salt = salt;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        public string Subject { get; private set; }
 | 
				
			||||||
 | 
					        public string UserName { get; private set; }
 | 
				
			||||||
 | 
					        public string Hash { get; private set; }
 | 
				
			||||||
 | 
					        public string Salt { get; private set; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										30
									
								
								src/Ocelot/Configuration/QoSOptions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/Ocelot/Configuration/QoSOptions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using Polly.Timeout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class QoSOptions
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public QoSOptions(
 | 
				
			||||||
 | 
					            int exceptionsAllowedBeforeBreaking, 
 | 
				
			||||||
 | 
					            int durationofBreak, 
 | 
				
			||||||
 | 
					            int timeoutValue, 
 | 
				
			||||||
 | 
					            TimeoutStrategy timeoutStrategy = TimeoutStrategy.Pessimistic)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ExceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking;
 | 
				
			||||||
 | 
					            DurationOfBreak = TimeSpan.FromMilliseconds(durationofBreak);
 | 
				
			||||||
 | 
					            TimeoutValue = TimeSpan.FromMilliseconds(timeoutValue);
 | 
				
			||||||
 | 
					            TimeoutStrategy = timeoutStrategy;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					         
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public int ExceptionsAllowedBeforeBreaking { get; private set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public TimeSpan DurationOfBreak { get; private set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public TimeSpan TimeoutValue { get; private set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public TimeoutStrategy TimeoutStrategy { get; private set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										83
									
								
								src/Ocelot/Configuration/RateLimitOptions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/Ocelot/Configuration/RateLimitOptions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,83 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /// <summary>
 | 
				
			||||||
 | 
					    /// RateLimit Options
 | 
				
			||||||
 | 
					    /// </summary>
 | 
				
			||||||
 | 
					    public class RateLimitOptions
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public RateLimitOptions(bool enbleRateLimiting, string clientIdHeader, List<string> clientWhitelist,bool disableRateLimitHeaders,
 | 
				
			||||||
 | 
					            string quotaExceededMessage, string rateLimitCounterPrefix, RateLimitRule rateLimitRule, int httpStatusCode)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            EnableRateLimiting = enbleRateLimiting;
 | 
				
			||||||
 | 
					            ClientIdHeader = clientIdHeader;
 | 
				
			||||||
 | 
					            ClientWhitelist = clientWhitelist?? new List<string>();
 | 
				
			||||||
 | 
					            DisableRateLimitHeaders = disableRateLimitHeaders;
 | 
				
			||||||
 | 
					            QuotaExceededMessage = quotaExceededMessage;
 | 
				
			||||||
 | 
					            RateLimitCounterPrefix = rateLimitCounterPrefix;
 | 
				
			||||||
 | 
					            RateLimitRule = rateLimitRule;
 | 
				
			||||||
 | 
					            HttpStatusCode = httpStatusCode;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public RateLimitRule RateLimitRule { get;  private set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public List<string> ClientWhitelist { get; private set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Gets or sets the HTTP header that holds the client identifier, by default is X-ClientId
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public string ClientIdHeader { get; private set; }  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Gets or sets the HTTP Status code returned when rate limiting occurs, by default value is set to 429 (Too Many Requests)
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public int HttpStatusCode { get; private set; } 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Gets or sets a value that will be used as a formatter for the QuotaExceeded response message.
 | 
				
			||||||
 | 
					        /// If none specified the default will be: 
 | 
				
			||||||
 | 
					        /// API calls quota exceeded! maximum admitted {0} per {1}
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public string QuotaExceededMessage { get; private set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Gets or sets the counter prefix, used to compose the rate limit counter cache key
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public string RateLimitCounterPrefix { get; private set; }  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Enables endpoint rate limiting based URL path and HTTP verb
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public bool EnableRateLimiting { get; private set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Disables X-Rate-Limit and Rety-After headers
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public bool DisableRateLimitHeaders { get; private set; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public class RateLimitRule
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public RateLimitRule(string period, TimeSpan periodTimespan, long limit)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Period = period;
 | 
				
			||||||
 | 
					            PeriodTimespan = periodTimespan;
 | 
				
			||||||
 | 
					            Limit = limit;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Rate limit period as in 1s, 1m, 1h,1d
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public string Period { get; private set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public TimeSpan PeriodTimespan { get; private set; }
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Maximum number of requests that a client can make in a defined period
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public long Limit { get; private set; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,16 +1,43 @@
 | 
				
			|||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Net.Http;
 | 
				
			||||||
 | 
					using Ocelot.Values;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.Configuration
 | 
					namespace Ocelot.Configuration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class ReRoute
 | 
					    public class ReRoute
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public ReRoute(string downstreamTemplate, string upstreamTemplate, string upstreamHttpMethod, string upstreamTemplatePattern, 
 | 
					        public ReRoute(PathTemplate downstreamPathTemplate, 
 | 
				
			||||||
            bool isAuthenticated, AuthenticationOptions authenticationOptions, List<ClaimToThing> configurationHeaderExtractorProperties, 
 | 
					            PathTemplate upstreamTemplate, 
 | 
				
			||||||
            List<ClaimToThing> claimsToClaims, Dictionary<string, string> routeClaimsRequirement, bool isAuthorised, List<ClaimToThing> claimsToQueries, 
 | 
					            HttpMethod upstreamHttpMethod, 
 | 
				
			||||||
            string requestIdKey, bool isCached, CacheOptions fileCacheOptions)
 | 
					            string upstreamTemplatePattern, 
 | 
				
			||||||
 | 
					            bool isAuthenticated, 
 | 
				
			||||||
 | 
					            AuthenticationOptions authenticationOptions, 
 | 
				
			||||||
 | 
					            List<ClaimToThing> configurationHeaderExtractorProperties, 
 | 
				
			||||||
 | 
					            List<ClaimToThing> claimsToClaims, 
 | 
				
			||||||
 | 
					            Dictionary<string, string> routeClaimsRequirement, 
 | 
				
			||||||
 | 
					            bool isAuthorised, 
 | 
				
			||||||
 | 
					            List<ClaimToThing> claimsToQueries, 
 | 
				
			||||||
 | 
					            string requestIdKey, 
 | 
				
			||||||
 | 
					            bool isCached, 
 | 
				
			||||||
 | 
					            CacheOptions fileCacheOptions, 
 | 
				
			||||||
 | 
					            string downstreamScheme, 
 | 
				
			||||||
 | 
					            string loadBalancer, 
 | 
				
			||||||
 | 
					            string downstreamHost, 
 | 
				
			||||||
 | 
					            int downstreamPort, 
 | 
				
			||||||
 | 
					            string reRouteKey, 
 | 
				
			||||||
 | 
					            ServiceProviderConfiguraion serviceProviderConfiguraion,
 | 
				
			||||||
 | 
					            bool isQos,
 | 
				
			||||||
 | 
					            QoSOptions qos,
 | 
				
			||||||
 | 
					            bool enableRateLimit,
 | 
				
			||||||
 | 
					            RateLimitOptions ratelimitOptions)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            DownstreamTemplate = downstreamTemplate;
 | 
					            ReRouteKey = reRouteKey;
 | 
				
			||||||
            UpstreamTemplate = upstreamTemplate;
 | 
					            ServiceProviderConfiguraion = serviceProviderConfiguraion;
 | 
				
			||||||
 | 
					            LoadBalancer = loadBalancer;
 | 
				
			||||||
 | 
					            DownstreamHost = downstreamHost;
 | 
				
			||||||
 | 
					            DownstreamPort = downstreamPort;
 | 
				
			||||||
 | 
					            DownstreamPathTemplate = downstreamPathTemplate;
 | 
				
			||||||
 | 
					            UpstreamPathTemplate = upstreamTemplate;
 | 
				
			||||||
            UpstreamHttpMethod = upstreamHttpMethod;
 | 
					            UpstreamHttpMethod = upstreamHttpMethod;
 | 
				
			||||||
            UpstreamTemplatePattern = upstreamTemplatePattern;
 | 
					            UpstreamTemplatePattern = upstreamTemplatePattern;
 | 
				
			||||||
            IsAuthenticated = isAuthenticated;
 | 
					            IsAuthenticated = isAuthenticated;
 | 
				
			||||||
@@ -26,12 +53,18 @@ namespace Ocelot.Configuration
 | 
				
			|||||||
                ?? new List<ClaimToThing>();
 | 
					                ?? new List<ClaimToThing>();
 | 
				
			||||||
            ClaimsToHeaders = configurationHeaderExtractorProperties 
 | 
					            ClaimsToHeaders = configurationHeaderExtractorProperties 
 | 
				
			||||||
                ?? new List<ClaimToThing>();
 | 
					                ?? new List<ClaimToThing>();
 | 
				
			||||||
 | 
					                DownstreamScheme = downstreamScheme;
 | 
				
			||||||
 | 
					            IsQos = isQos;
 | 
				
			||||||
 | 
					            QosOptions = qos;
 | 
				
			||||||
 | 
					            EnableEndpointRateLimiting = enableRateLimit;
 | 
				
			||||||
 | 
					            RateLimitOptions = ratelimitOptions;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string DownstreamTemplate { get; private set; }
 | 
					        public string ReRouteKey {get;private set;}
 | 
				
			||||||
        public string UpstreamTemplate { get; private set; }
 | 
					        public PathTemplate DownstreamPathTemplate { get; private set; }
 | 
				
			||||||
 | 
					        public PathTemplate UpstreamPathTemplate { get; private set; }
 | 
				
			||||||
        public string UpstreamTemplatePattern { get; private set; }
 | 
					        public string UpstreamTemplatePattern { get; private set; }
 | 
				
			||||||
        public string UpstreamHttpMethod { get; private set; }
 | 
					        public HttpMethod UpstreamHttpMethod { get; private set; }
 | 
				
			||||||
        public bool IsAuthenticated { get; private set; }
 | 
					        public bool IsAuthenticated { get; private set; }
 | 
				
			||||||
        public bool IsAuthorised { get; private set; }
 | 
					        public bool IsAuthorised { get; private set; }
 | 
				
			||||||
        public AuthenticationOptions AuthenticationOptions { get; private set; }
 | 
					        public AuthenticationOptions AuthenticationOptions { get; private set; }
 | 
				
			||||||
@@ -42,5 +75,14 @@ namespace Ocelot.Configuration
 | 
				
			|||||||
        public string RequestIdKey { get; private set; }
 | 
					        public string RequestIdKey { get; private set; }
 | 
				
			||||||
        public bool IsCached { get; private set; }
 | 
					        public bool IsCached { get; private set; }
 | 
				
			||||||
        public CacheOptions FileCacheOptions { get; private set; }
 | 
					        public CacheOptions FileCacheOptions { get; private set; }
 | 
				
			||||||
 | 
					        public string DownstreamScheme {get;private set;}
 | 
				
			||||||
 | 
					        public bool IsQos { get; private set; }
 | 
				
			||||||
 | 
					        public QoSOptions QosOptions { get; private set; }
 | 
				
			||||||
 | 
					        public string LoadBalancer {get;private set;}
 | 
				
			||||||
 | 
					        public string DownstreamHost { get; private set; }
 | 
				
			||||||
 | 
					        public int DownstreamPort { get; private set; }
 | 
				
			||||||
 | 
					        public ServiceProviderConfiguraion ServiceProviderConfiguraion { get; private set; }
 | 
				
			||||||
 | 
					        public bool EnableEndpointRateLimiting { get; private set; }
 | 
				
			||||||
 | 
					        public RateLimitOptions RateLimitOptions { get; private set; }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using Newtonsoft.Json;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Repository
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class FileConfigurationRepository : IFileConfigurationRepository
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private static readonly object _lock = new object();
 | 
				
			||||||
 | 
					        public Response<FileConfiguration> Get()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var configFilePath = $"{AppContext.BaseDirectory}/configuration.json";
 | 
				
			||||||
 | 
					            string json = string.Empty;
 | 
				
			||||||
 | 
					            lock(_lock)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                json = System.IO.File.ReadAllText(configFilePath);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            var fileConfiguration = JsonConvert.DeserializeObject<FileConfiguration>(json);
 | 
				
			||||||
 | 
					            return new OkResponse<FileConfiguration>(fileConfiguration);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public Response Set(FileConfiguration fileConfiguration)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var configurationPath = $"{AppContext.BaseDirectory}/configuration.json";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            lock(_lock)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (System.IO.File.Exists(configurationPath))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    System.IO.File.Delete(configurationPath);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                System.IO.File.WriteAllText(configurationPath, jsonConfiguration);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            return new OkResponse();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Repository
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public interface IFileConfigurationRepository
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Response<FileConfiguration> Get();
 | 
				
			||||||
 | 
					        Response Set(FileConfiguration fileConfiguration);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										25
									
								
								src/Ocelot/Configuration/ServiceProviderConfiguraion.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/Ocelot/Configuration/ServiceProviderConfiguraion.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					namespace Ocelot.Configuration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class ServiceProviderConfiguraion
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public ServiceProviderConfiguraion(string serviceName, string downstreamHost,
 | 
				
			||||||
 | 
					            int downstreamPort, bool useServiceDiscovery, string serviceDiscoveryProvider, string serviceProviderHost, int serviceProviderPort)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ServiceName = serviceName;
 | 
				
			||||||
 | 
					            DownstreamHost = downstreamHost;
 | 
				
			||||||
 | 
					            DownstreamPort = downstreamPort;
 | 
				
			||||||
 | 
					            UseServiceDiscovery = useServiceDiscovery;
 | 
				
			||||||
 | 
					            ServiceDiscoveryProvider = serviceDiscoveryProvider;
 | 
				
			||||||
 | 
					            ServiceProviderHost = serviceProviderHost;
 | 
				
			||||||
 | 
					            ServiceProviderPort = serviceProviderPort;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string ServiceName { get; }
 | 
				
			||||||
 | 
					        public string DownstreamHost { get; }
 | 
				
			||||||
 | 
					        public int DownstreamPort { get; }
 | 
				
			||||||
 | 
					        public bool UseServiceDiscovery { get; }
 | 
				
			||||||
 | 
					        public string ServiceDiscoveryProvider { get; }
 | 
				
			||||||
 | 
					        public string ServiceProviderHost { get; private set; }
 | 
				
			||||||
 | 
					        public int ServiceProviderPort { get; private set; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										42
									
								
								src/Ocelot/Configuration/Setter/FileConfigurationSetter.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/Ocelot/Configuration/Setter/FileConfigurationSetter.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Creator;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Repository;
 | 
				
			||||||
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Setter
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class FileConfigurationSetter : IFileConfigurationSetter
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private readonly IOcelotConfigurationRepository _configRepo;
 | 
				
			||||||
 | 
					        private readonly IOcelotConfigurationCreator _configCreator;
 | 
				
			||||||
 | 
					        private readonly IFileConfigurationRepository _repo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public  FileConfigurationSetter(IOcelotConfigurationRepository configRepo, 
 | 
				
			||||||
 | 
					            IOcelotConfigurationCreator configCreator, IFileConfigurationRepository repo)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _configRepo = configRepo;
 | 
				
			||||||
 | 
					            _configCreator = configCreator;
 | 
				
			||||||
 | 
					            _repo = repo;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task<Response> Set(FileConfiguration fileConfig)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var response = _repo.Set(fileConfig);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if(response.IsError)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return new ErrorResponse(response.Errors);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var config = await _configCreator.Create(fileConfig);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if(!config.IsError)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                _configRepo.AddOrReplace(config.Data);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return new ErrorResponse(config.Errors);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										11
									
								
								src/Ocelot/Configuration/Setter/IFileConfigurationSetter.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/Ocelot/Configuration/Setter/IFileConfigurationSetter.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Setter
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public interface IFileConfigurationSetter
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Task<Response> Set(FileConfiguration config);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					using Ocelot.Errors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Validator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class DownstreamPathTemplateAlreadyUsedError : Error
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public DownstreamPathTemplateAlreadyUsedError(string message) : base(message, OcelotErrorCode.DownstreampathTemplateAlreadyUsedError)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					using Ocelot.Errors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Validator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class DownstreamPathTemplateContainsSchemeError : Error
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public DownstreamPathTemplateContainsSchemeError(string message) 
 | 
				
			||||||
 | 
					            : base(message, OcelotErrorCode.DownstreamPathTemplateContainsSchemeError)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,11 +0,0 @@
 | 
				
			|||||||
using Ocelot.Errors;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Ocelot.Configuration.Validator
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    public class DownstreamTemplateAlreadyUsedError : Error
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        public DownstreamTemplateAlreadyUsedError(string message) : base(message, OcelotErrorCode.DownstreamTemplateAlreadyUsedError)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -26,6 +26,13 @@ namespace Ocelot.Configuration.Validator
 | 
				
			|||||||
                return new OkResponse<ConfigurationValidationResult>(result);
 | 
					                return new OkResponse<ConfigurationValidationResult>(result);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            result = CheckForReRoutesContainingDownstreamSchemeInDownstreamPathTemplate(configuration);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (result.IsError)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return new OkResponse<ConfigurationValidationResult>(result);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return new OkResponse<ConfigurationValidationResult>(result);
 | 
					            return new OkResponse<ConfigurationValidationResult>(result);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -47,7 +54,7 @@ namespace Ocelot.Configuration.Validator
 | 
				
			|||||||
                    continue;
 | 
					                    continue;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var error = new UnsupportedAuthenticationProviderError($"{reRoute.AuthenticationOptions?.Provider} is unsupported authentication provider, upstream template is {reRoute.UpstreamTemplate}, upstream method is {reRoute.UpstreamHttpMethod}");
 | 
					                var error = new UnsupportedAuthenticationProviderError($"{reRoute.AuthenticationOptions?.Provider} is unsupported authentication provider, upstream template is {reRoute.UpstreamPathTemplate}, upstream method is {reRoute.UpstreamHttpMethod}");
 | 
				
			||||||
                errors.Add(error);
 | 
					                errors.Add(error);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -63,21 +70,42 @@ namespace Ocelot.Configuration.Validator
 | 
				
			|||||||
            return Enum.TryParse(provider, true, out supportedProvider);
 | 
					            return Enum.TryParse(provider, true, out supportedProvider);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private ConfigurationValidationResult CheckForReRoutesContainingDownstreamSchemeInDownstreamPathTemplate(FileConfiguration configuration)
 | 
				
			||||||
 | 
					        {   
 | 
				
			||||||
 | 
					            var errors = new List<Error>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            foreach(var reRoute in configuration.ReRoutes)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if(reRoute.DownstreamPathTemplate.Contains("https://")
 | 
				
			||||||
 | 
					                || reRoute.DownstreamPathTemplate.Contains("http://"))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    errors.Add(new DownstreamPathTemplateContainsSchemeError($"{reRoute.DownstreamPathTemplate} contains scheme"));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if(errors.Any())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return new ConfigurationValidationResult(true, errors);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return new ConfigurationValidationResult(false, errors);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private ConfigurationValidationResult CheckForDupliateReRoutes(FileConfiguration configuration)
 | 
					        private ConfigurationValidationResult CheckForDupliateReRoutes(FileConfiguration configuration)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var hasDupes = configuration.ReRoutes
 | 
					            var hasDupes = configuration.ReRoutes
 | 
				
			||||||
                   .GroupBy(x => new { x.UpstreamTemplate, x.UpstreamHttpMethod }).Any(x => x.Skip(1).Any());
 | 
					                   .GroupBy(x => new { x.UpstreamPathTemplate, x.UpstreamHttpMethod }).Any(x => x.Skip(1).Any());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!hasDupes)
 | 
					            if (!hasDupes)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                return new ConfigurationValidationResult(false);
 | 
					                return new ConfigurationValidationResult(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var dupes = configuration.ReRoutes.GroupBy(x => new { x.UpstreamTemplate, x.UpstreamHttpMethod })
 | 
					            var dupes = configuration.ReRoutes.GroupBy(x => new { x.UpstreamPathTemplate, x.UpstreamHttpMethod })
 | 
				
			||||||
                               .Where(x => x.Skip(1).Any());
 | 
					                               .Where(x => x.Skip(1).Any());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var errors = dupes
 | 
					            var errors = dupes
 | 
				
			||||||
                .Select(d => new DownstreamTemplateAlreadyUsedError(string.Format("Duplicate DownstreamTemplate: {0}", d.Key.UpstreamTemplate)))
 | 
					                .Select(d => new DownstreamPathTemplateAlreadyUsedError(string.Format("Duplicate DownstreamPath: {0}", d.Key.UpstreamPathTemplate)))
 | 
				
			||||||
                .Cast<Error>()
 | 
					                .Cast<Error>()
 | 
				
			||||||
                .ToList();
 | 
					                .ToList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										49
									
								
								src/Ocelot/Controllers/FileConfigurationController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/Ocelot/Controllers/FileConfigurationController.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
				
			|||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Authorization;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Provider;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Setter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Controllers
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    [Authorize]
 | 
				
			||||||
 | 
					    [Route("configuration")]
 | 
				
			||||||
 | 
					    public class FileConfigurationController : Controller
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private readonly IFileConfigurationProvider _configGetter;
 | 
				
			||||||
 | 
					        private readonly IFileConfigurationSetter _configSetter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public FileConfigurationController(IFileConfigurationProvider getFileConfig, IFileConfigurationSetter configSetter)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _configGetter = getFileConfig;
 | 
				
			||||||
 | 
					            _configSetter = configSetter;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [HttpGet]
 | 
				
			||||||
 | 
					        public IActionResult Get()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var response = _configGetter.Get();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if(response.IsError)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return new BadRequestObjectResult(response.Errors);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return new OkObjectResult(response.Data);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [HttpPost]
 | 
				
			||||||
 | 
					        public async Task<IActionResult> Post([FromBody]FileConfiguration fileConfiguration)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var response = await _configSetter.Set(fileConfiguration);
 | 
				
			||||||
 | 
					              
 | 
				
			||||||
 | 
					            if(response.IsError)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return new BadRequestObjectResult(response.Errors);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return new OkObjectResult(fileConfiguration);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,64 +1,121 @@
 | 
				
			|||||||
using System;
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
using System.Net.Http;
 | 
					using System.Net.Http;
 | 
				
			||||||
using CacheManager.Core;
 | 
					using CacheManager.Core;
 | 
				
			||||||
 | 
					using IdentityServer4.Models;
 | 
				
			||||||
using Microsoft.AspNetCore.Http;
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
using Microsoft.Extensions.Configuration;
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
using Microsoft.Extensions.DependencyInjection;
 | 
					using Microsoft.Extensions.DependencyInjection;
 | 
				
			||||||
using Microsoft.Extensions.Logging;
 | 
					 | 
				
			||||||
using Ocelot.Authentication.Handler.Creator;
 | 
					using Ocelot.Authentication.Handler.Creator;
 | 
				
			||||||
using Ocelot.Authentication.Handler.Factory;
 | 
					using Ocelot.Authentication.Handler.Factory;
 | 
				
			||||||
using Ocelot.Authorisation;
 | 
					using Ocelot.Authorisation;
 | 
				
			||||||
using Ocelot.Cache;
 | 
					using Ocelot.Cache;
 | 
				
			||||||
using Ocelot.Claims;
 | 
					using Ocelot.Claims;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Authentication;
 | 
				
			||||||
using Ocelot.Configuration.Creator;
 | 
					using Ocelot.Configuration.Creator;
 | 
				
			||||||
using Ocelot.Configuration.File;
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
using Ocelot.Configuration.Parser;
 | 
					using Ocelot.Configuration.Parser;
 | 
				
			||||||
using Ocelot.Configuration.Provider;
 | 
					using Ocelot.Configuration.Provider;
 | 
				
			||||||
using Ocelot.Configuration.Repository;
 | 
					using Ocelot.Configuration.Repository;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Setter;
 | 
				
			||||||
using Ocelot.Configuration.Validator;
 | 
					using Ocelot.Configuration.Validator;
 | 
				
			||||||
using Ocelot.DownstreamRouteFinder.Finder;
 | 
					using Ocelot.DownstreamRouteFinder.Finder;
 | 
				
			||||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
 | 
					using Ocelot.DownstreamRouteFinder.UrlMatcher;
 | 
				
			||||||
 | 
					using Ocelot.DownstreamUrlCreator;
 | 
				
			||||||
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
 | 
					using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
 | 
				
			||||||
using Ocelot.Headers;
 | 
					using Ocelot.Headers;
 | 
				
			||||||
using Ocelot.Infrastructure.Claims.Parser;
 | 
					using Ocelot.Infrastructure.Claims.Parser;
 | 
				
			||||||
using Ocelot.Infrastructure.RequestData;
 | 
					using Ocelot.Infrastructure.RequestData;
 | 
				
			||||||
 | 
					using Ocelot.LoadBalancer.LoadBalancers;
 | 
				
			||||||
using Ocelot.Logging;
 | 
					using Ocelot.Logging;
 | 
				
			||||||
 | 
					using Ocelot.Middleware;
 | 
				
			||||||
using Ocelot.QueryStrings;
 | 
					using Ocelot.QueryStrings;
 | 
				
			||||||
using Ocelot.Request.Builder;
 | 
					using Ocelot.Request.Builder;
 | 
				
			||||||
using Ocelot.Requester;
 | 
					using Ocelot.Requester;
 | 
				
			||||||
 | 
					using Ocelot.Requester.QoS;
 | 
				
			||||||
using Ocelot.Responder;
 | 
					using Ocelot.Responder;
 | 
				
			||||||
 | 
					using Ocelot.ServiceDiscovery;
 | 
				
			||||||
 | 
					using FileConfigurationProvider = Ocelot.Configuration.Provider.FileConfigurationProvider;
 | 
				
			||||||
 | 
					using Ocelot.RateLimit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.DependencyInjection
 | 
					namespace Ocelot.DependencyInjection
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public static class ServiceCollectionExtensions
 | 
					    public static class ServiceCollectionExtensions
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static IServiceCollection AddOcelotOutputCaching(this IServiceCollection services, Action<ConfigurationBuilderCachePart> settings)
 | 
					        public static IServiceCollection AddOcelotOutputCaching(this IServiceCollection services, Action<ConfigurationBuilderCachePart> settings)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var cacheManagerOutputCache = CacheFactory.Build<HttpResponseMessage>("OcelotOutputCache", settings);
 | 
					            var cacheManagerOutputCache = CacheFactory.Build<HttpResponseMessage>("OcelotOutputCache", settings);
 | 
				
			||||||
            var ocelotCacheManager = new OcelotCacheManagerCache<HttpResponseMessage>(cacheManagerOutputCache);
 | 
					            var ocelotCacheManager = new OcelotCacheManagerCache<HttpResponseMessage>(cacheManagerOutputCache);
 | 
				
			||||||
 | 
					 | 
				
			||||||
            services.AddSingleton<ICacheManager<HttpResponseMessage>>(cacheManagerOutputCache);
 | 
					            services.AddSingleton<ICacheManager<HttpResponseMessage>>(cacheManagerOutputCache);
 | 
				
			||||||
            services.AddSingleton<IOcelotCache<HttpResponseMessage>>(ocelotCacheManager);
 | 
					            services.AddSingleton<IOcelotCache<HttpResponseMessage>>(ocelotCacheManager);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return services;
 | 
					            return services;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        public static IServiceCollection AddOcelotFileConfiguration(this IServiceCollection services, IConfigurationRoot configurationRoot)
 | 
					
 | 
				
			||||||
 | 
					        public static IServiceCollection AddOcelot(this IServiceCollection services, IConfigurationRoot configurationRoot)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            services.Configure<FileConfiguration>(configurationRoot);
 | 
					            services.Configure<FileConfiguration>(configurationRoot);
 | 
				
			||||||
 | 
					 | 
				
			||||||
            services.AddSingleton<IOcelotConfigurationCreator, FileOcelotConfigurationCreator>();
 | 
					            services.AddSingleton<IOcelotConfigurationCreator, FileOcelotConfigurationCreator>();
 | 
				
			||||||
            services.AddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>();
 | 
					            services.AddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>();
 | 
				
			||||||
            services.AddSingleton<IConfigurationValidator, FileConfigurationValidator>();
 | 
					            services.AddSingleton<IConfigurationValidator, FileConfigurationValidator>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IBaseUrlFinder, BaseUrlFinder>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return services;
 | 
					            var identityServerConfiguration = IdentityServerConfigurationCreator.GetIdentityServerConfiguration();
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if(identityServerConfiguration != null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                services.AddSingleton<IIdentityServerConfiguration>(identityServerConfiguration);
 | 
				
			||||||
 | 
					                services.AddSingleton<IHashMatcher, HashMatcher>();
 | 
				
			||||||
 | 
					                services.AddIdentityServer()
 | 
				
			||||||
 | 
					                    .AddTemporarySigningCredential()
 | 
				
			||||||
 | 
					                    .AddInMemoryApiResources(new List<ApiResource>
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        new ApiResource
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            Name = identityServerConfiguration.ApiName,
 | 
				
			||||||
 | 
					                            Description = identityServerConfiguration.Description,
 | 
				
			||||||
 | 
					                            Enabled = identityServerConfiguration.Enabled,
 | 
				
			||||||
 | 
					                            DisplayName = identityServerConfiguration.ApiName,
 | 
				
			||||||
 | 
					                            Scopes = identityServerConfiguration.AllowedScopes.Select(x => new Scope(x)).ToList(),
 | 
				
			||||||
 | 
					                            ApiSecrets = new List<Secret>
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                new Secret
 | 
				
			||||||
 | 
					                                {
 | 
				
			||||||
 | 
					                                    Value = identityServerConfiguration.ApiSecret.Sha256()
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                    .AddInMemoryClients(new List<Client>
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        new Client
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            ClientId = identityServerConfiguration.ApiName,
 | 
				
			||||||
 | 
					                            AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
 | 
				
			||||||
 | 
					                            ClientSecrets = new List<Secret> {new Secret(identityServerConfiguration.ApiSecret.Sha256())},
 | 
				
			||||||
 | 
					                            AllowedScopes = identityServerConfiguration.AllowedScopes,
 | 
				
			||||||
 | 
					                            AccessTokenType = identityServerConfiguration.AccessTokenType,
 | 
				
			||||||
 | 
					                            Enabled = identityServerConfiguration.Enabled,
 | 
				
			||||||
 | 
					                            RequireClientSecret = identityServerConfiguration.RequireClientSecret
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }).AddResourceOwnerValidator<OcelotResourceOwnerPasswordValidator>();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        public static IServiceCollection AddOcelot(this IServiceCollection services)
 | 
					            services.AddMvcCore()
 | 
				
			||||||
        {
 | 
					                .AddAuthorization()
 | 
				
			||||||
            services.AddMvcCore().AddJsonFormatters();
 | 
					                .AddJsonFormatters();
 | 
				
			||||||
            services.AddLogging();
 | 
					            services.AddLogging();
 | 
				
			||||||
 | 
					            services.AddSingleton<IFileConfigurationRepository, FileConfigurationRepository>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IFileConfigurationSetter, FileConfigurationSetter>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IFileConfigurationProvider, FileConfigurationProvider>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IQosProviderHouse, QosProviderHouse>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IQoSProviderFactory, QoSProviderFactory>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IServiceDiscoveryProviderFactory, ServiceDiscoveryProviderFactory>();
 | 
				
			||||||
 | 
					            services.AddSingleton<ILoadBalancerFactory, LoadBalancerFactory>();
 | 
				
			||||||
 | 
					            services.AddSingleton<ILoadBalancerHouse, LoadBalancerHouse>();
 | 
				
			||||||
            services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
 | 
					            services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IUrlBuilder, UrlBuilder>();
 | 
				
			||||||
            services.AddSingleton<IRemoveOutputHeaders, RemoveOutputHeaders>();
 | 
					            services.AddSingleton<IRemoveOutputHeaders, RemoveOutputHeaders>();
 | 
				
			||||||
            services.AddSingleton<IOcelotConfigurationProvider, OcelotConfigurationProvider>();
 | 
					            services.AddSingleton<IOcelotConfigurationProvider, OcelotConfigurationProvider>();
 | 
				
			||||||
            services.AddSingleton<IClaimToThingConfigurationParser, ClaimToThingConfigurationParser>();
 | 
					            services.AddSingleton<IClaimToThingConfigurationParser, ClaimToThingConfigurationParser>();
 | 
				
			||||||
@@ -69,7 +126,7 @@ namespace Ocelot.DependencyInjection
 | 
				
			|||||||
            services.AddSingleton<IClaimsParser, ClaimsParser>();
 | 
					            services.AddSingleton<IClaimsParser, ClaimsParser>();
 | 
				
			||||||
            services.AddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>();
 | 
					            services.AddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>();
 | 
				
			||||||
            services.AddSingleton<IUrlPathPlaceholderNameAndValueFinder, UrlPathPlaceholderNameAndValueFinder>();
 | 
					            services.AddSingleton<IUrlPathPlaceholderNameAndValueFinder, UrlPathPlaceholderNameAndValueFinder>();
 | 
				
			||||||
            services.AddSingleton<IDownstreamUrlPathPlaceholderReplacer, DownstreamUrlPathPlaceholderReplacer>();
 | 
					            services.AddSingleton<IDownstreamPathPlaceholderReplacer, DownstreamTemplatePathPlaceholderReplacer>();
 | 
				
			||||||
            services.AddSingleton<IDownstreamRouteFinder, DownstreamRouteFinder.Finder.DownstreamRouteFinder>();
 | 
					            services.AddSingleton<IDownstreamRouteFinder, DownstreamRouteFinder.Finder.DownstreamRouteFinder>();
 | 
				
			||||||
            services.AddSingleton<IHttpRequester, HttpClientHttpRequester>();
 | 
					            services.AddSingleton<IHttpRequester, HttpClientHttpRequester>();
 | 
				
			||||||
            services.AddSingleton<IHttpResponder, HttpContextResponder>();
 | 
					            services.AddSingleton<IHttpResponder, HttpContextResponder>();
 | 
				
			||||||
@@ -77,12 +134,13 @@ namespace Ocelot.DependencyInjection
 | 
				
			|||||||
            services.AddSingleton<IErrorsToHttpStatusCodeMapper, ErrorsToHttpStatusCodeMapper>();
 | 
					            services.AddSingleton<IErrorsToHttpStatusCodeMapper, ErrorsToHttpStatusCodeMapper>();
 | 
				
			||||||
            services.AddSingleton<IAuthenticationHandlerFactory, AuthenticationHandlerFactory>();
 | 
					            services.AddSingleton<IAuthenticationHandlerFactory, AuthenticationHandlerFactory>();
 | 
				
			||||||
            services.AddSingleton<IAuthenticationHandlerCreator, AuthenticationHandlerCreator>();
 | 
					            services.AddSingleton<IAuthenticationHandlerCreator, AuthenticationHandlerCreator>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IRateLimitCounterHandler, MemoryCacheRateLimitCounterHandler>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc
 | 
					            // see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc
 | 
				
			||||||
            // could maybe use a scoped data repository
 | 
					            // could maybe use a scoped data repository
 | 
				
			||||||
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
 | 
					            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
 | 
				
			||||||
            services.AddScoped<IRequestScopedDataRepository, HttpDataRepository>();
 | 
					            services.AddScoped<IRequestScopedDataRepository, HttpDataRepository>();
 | 
				
			||||||
 | 
					            services.AddMemoryCache();
 | 
				
			||||||
            return services;
 | 
					            return services;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,150 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Net.Http;
 | 
				
			||||||
 | 
					using CacheManager.Core;
 | 
				
			||||||
 | 
					using IdentityServer4.Models;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.DependencyInjection;
 | 
				
			||||||
 | 
					using Ocelot.Authentication.Handler.Creator;
 | 
				
			||||||
 | 
					using Ocelot.Authentication.Handler.Factory;
 | 
				
			||||||
 | 
					using Ocelot.Authorisation;
 | 
				
			||||||
 | 
					using Ocelot.Cache;
 | 
				
			||||||
 | 
					using Ocelot.Claims;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Authentication;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Creator;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Parser;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Provider;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Repository;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Setter;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Validator;
 | 
				
			||||||
 | 
					using Ocelot.DownstreamRouteFinder.Finder;
 | 
				
			||||||
 | 
					using Ocelot.DownstreamRouteFinder.UrlMatcher;
 | 
				
			||||||
 | 
					using Ocelot.DownstreamUrlCreator;
 | 
				
			||||||
 | 
					using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
 | 
				
			||||||
 | 
					using Ocelot.Headers;
 | 
				
			||||||
 | 
					using Ocelot.Infrastructure.Claims.Parser;
 | 
				
			||||||
 | 
					using Ocelot.Infrastructure.RequestData;
 | 
				
			||||||
 | 
					using Ocelot.LoadBalancer.LoadBalancers;
 | 
				
			||||||
 | 
					using Ocelot.Logging;
 | 
				
			||||||
 | 
					using Ocelot.Middleware;
 | 
				
			||||||
 | 
					using Ocelot.QueryStrings;
 | 
				
			||||||
 | 
					using Ocelot.Request.Builder;
 | 
				
			||||||
 | 
					using Ocelot.Requester;
 | 
				
			||||||
 | 
					using Ocelot.Requester.QoS;
 | 
				
			||||||
 | 
					using Ocelot.Responder;
 | 
				
			||||||
 | 
					using Ocelot.ServiceDiscovery;
 | 
				
			||||||
 | 
					<<<<<<< HEAD
 | 
				
			||||||
 | 
					using FileConfigurationProvider = Ocelot.Configuration.Provider.FileConfigurationProvider;
 | 
				
			||||||
 | 
					=======
 | 
				
			||||||
 | 
					using Ocelot.RateLimit;
 | 
				
			||||||
 | 
					>>>>>>> develop
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.DependencyInjection
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public static class ServiceCollectionExtensions
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public static IServiceCollection AddOcelotOutputCaching(this IServiceCollection services, Action<ConfigurationBuilderCachePart> settings)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var cacheManagerOutputCache = CacheFactory.Build<HttpResponseMessage>("OcelotOutputCache", settings);
 | 
				
			||||||
 | 
					            var ocelotCacheManager = new OcelotCacheManagerCache<HttpResponseMessage>(cacheManagerOutputCache);
 | 
				
			||||||
 | 
					            services.AddSingleton<ICacheManager<HttpResponseMessage>>(cacheManagerOutputCache);
 | 
				
			||||||
 | 
					            services.AddSingleton<IOcelotCache<HttpResponseMessage>>(ocelotCacheManager);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return services;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static IServiceCollection AddOcelot(this IServiceCollection services, IConfigurationRoot configurationRoot)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            services.Configure<FileConfiguration>(configurationRoot);
 | 
				
			||||||
 | 
					            services.AddSingleton<IOcelotConfigurationCreator, FileOcelotConfigurationCreator>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IConfigurationValidator, FileConfigurationValidator>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IBaseUrlFinder, BaseUrlFinder>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var identityServerConfiguration = IdentityServerConfigurationCreator.GetIdentityServerConfiguration();
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if(identityServerConfiguration != null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                services.AddSingleton<IIdentityServerConfiguration>(identityServerConfiguration);
 | 
				
			||||||
 | 
					                services.AddSingleton<IHashMatcher, HashMatcher>();
 | 
				
			||||||
 | 
					                services.AddIdentityServer()
 | 
				
			||||||
 | 
					                    .AddTemporarySigningCredential()
 | 
				
			||||||
 | 
					                    .AddInMemoryApiResources(new List<ApiResource>
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        new ApiResource
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            Name = identityServerConfiguration.ApiName,
 | 
				
			||||||
 | 
					                            Description = identityServerConfiguration.Description,
 | 
				
			||||||
 | 
					                            Enabled = identityServerConfiguration.Enabled,
 | 
				
			||||||
 | 
					                            DisplayName = identityServerConfiguration.ApiName,
 | 
				
			||||||
 | 
					                            Scopes = identityServerConfiguration.AllowedScopes.Select(x => new Scope(x)).ToList(),
 | 
				
			||||||
 | 
					                            ApiSecrets = new List<Secret>
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                new Secret
 | 
				
			||||||
 | 
					                                {
 | 
				
			||||||
 | 
					                                    Value = identityServerConfiguration.ApiSecret.Sha256()
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                    .AddInMemoryClients(new List<Client>
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        new Client
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            ClientId = identityServerConfiguration.ApiName,
 | 
				
			||||||
 | 
					                            AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
 | 
				
			||||||
 | 
					                            ClientSecrets = new List<Secret> {new Secret(identityServerConfiguration.ApiSecret.Sha256())},
 | 
				
			||||||
 | 
					                            AllowedScopes = identityServerConfiguration.AllowedScopes,
 | 
				
			||||||
 | 
					                            AccessTokenType = identityServerConfiguration.AccessTokenType,
 | 
				
			||||||
 | 
					                            Enabled = identityServerConfiguration.Enabled,
 | 
				
			||||||
 | 
					                            RequireClientSecret = identityServerConfiguration.RequireClientSecret
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }).AddResourceOwnerValidator<OcelotResourceOwnerPasswordValidator>();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					            services.AddMvcCore()
 | 
				
			||||||
 | 
					                .AddAuthorization()
 | 
				
			||||||
 | 
					                .AddJsonFormatters();
 | 
				
			||||||
 | 
					            services.AddLogging();
 | 
				
			||||||
 | 
					            services.AddSingleton<IFileConfigurationRepository, FileConfigurationRepository>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IFileConfigurationSetter, FileConfigurationSetter>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IFileConfigurationProvider, FileConfigurationProvider>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IQosProviderHouse, QosProviderHouse>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IQoSProviderFactory, QoSProviderFactory>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IServiceDiscoveryProviderFactory, ServiceDiscoveryProviderFactory>();
 | 
				
			||||||
 | 
					            services.AddSingleton<ILoadBalancerFactory, LoadBalancerFactory>();
 | 
				
			||||||
 | 
					            services.AddSingleton<ILoadBalancerHouse, LoadBalancerHouse>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IUrlBuilder, UrlBuilder>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IRemoveOutputHeaders, RemoveOutputHeaders>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IOcelotConfigurationProvider, OcelotConfigurationProvider>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IClaimToThingConfigurationParser, ClaimToThingConfigurationParser>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IAuthoriser, ClaimsAuthoriser>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IAddClaimsToRequest, AddClaimsToRequest>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IAddHeadersToRequest, AddHeadersToRequest>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IAddQueriesToRequest, AddQueriesToRequest>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IClaimsParser, ClaimsParser>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IUrlPathPlaceholderNameAndValueFinder, UrlPathPlaceholderNameAndValueFinder>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IDownstreamPathPlaceholderReplacer, DownstreamTemplatePathPlaceholderReplacer>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IDownstreamRouteFinder, DownstreamRouteFinder.Finder.DownstreamRouteFinder>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IHttpRequester, HttpClientHttpRequester>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IHttpResponder, HttpContextResponder>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IRequestCreator, HttpRequestCreator>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IErrorsToHttpStatusCodeMapper, ErrorsToHttpStatusCodeMapper>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IAuthenticationHandlerFactory, AuthenticationHandlerFactory>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IAuthenticationHandlerCreator, AuthenticationHandlerCreator>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IRateLimitCounterHandler, MemoryCacheRateLimitCounterHandler>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc
 | 
				
			||||||
 | 
					            // could maybe use a scoped data repository
 | 
				
			||||||
 | 
					            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
 | 
				
			||||||
 | 
					            services.AddScoped<IRequestScopedDataRepository, HttpDataRepository>();
 | 
				
			||||||
 | 
					            services.AddMemoryCache();
 | 
				
			||||||
 | 
					            return services;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -25,15 +25,22 @@ namespace Ocelot.DownstreamRouteFinder.Finder
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            var configuration = _configProvider.Get();
 | 
					            var configuration = _configProvider.Get();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var applicableReRoutes = configuration.Data.ReRoutes.Where(r => string.Equals(r.UpstreamHttpMethod, upstreamHttpMethod, StringComparison.CurrentCultureIgnoreCase));
 | 
					            var applicableReRoutes = configuration.Data.ReRoutes.Where(r => string.Equals(r.UpstreamHttpMethod.Method, upstreamHttpMethod, StringComparison.CurrentCultureIgnoreCase));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            foreach (var reRoute in applicableReRoutes)
 | 
					            foreach (var reRoute in applicableReRoutes)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 | 
					                if (upstreamUrlPath == reRoute.UpstreamTemplatePattern)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    var templateVariableNameAndValues = _urlPathPlaceholderNameAndValueFinder.Find(upstreamUrlPath, reRoute.UpstreamPathTemplate.Value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    return new OkResponse<DownstreamRoute>(new DownstreamRoute(templateVariableNameAndValues.Data, reRoute));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var urlMatch = _urlMatcher.Match(upstreamUrlPath, reRoute.UpstreamTemplatePattern);
 | 
					                var urlMatch = _urlMatcher.Match(upstreamUrlPath, reRoute.UpstreamTemplatePattern);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (urlMatch.Data.Match)
 | 
					                if (urlMatch.Data.Match)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var templateVariableNameAndValues = _urlPathPlaceholderNameAndValueFinder.Find(upstreamUrlPath, reRoute.UpstreamTemplate);
 | 
					                    var templateVariableNameAndValues = _urlPathPlaceholderNameAndValueFinder.Find(upstreamUrlPath, reRoute.UpstreamPathTemplate.Value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    return new OkResponse<DownstreamRoute>(new DownstreamRoute(templateVariableNameAndValues.Data, reRoute));
 | 
					                    return new OkResponse<DownstreamRoute>(new DownstreamRoute(templateVariableNameAndValues.Data, reRoute));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,5 @@
 | 
				
			|||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Microsoft.AspNetCore.Http;
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
using Microsoft.Extensions.Logging;
 | 
					 | 
				
			||||||
using Ocelot.DownstreamRouteFinder.Finder;
 | 
					using Ocelot.DownstreamRouteFinder.Finder;
 | 
				
			||||||
using Ocelot.Infrastructure.RequestData;
 | 
					using Ocelot.Infrastructure.RequestData;
 | 
				
			||||||
using Ocelot.Logging;
 | 
					using Ocelot.Logging;
 | 
				
			||||||
@@ -44,7 +43,7 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
 | 
				
			|||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _logger.LogDebug("downstream template is {downstreamRoute.Data.ReRoute.DownstreamTemplate}", downstreamRoute.Data.ReRoute.DownstreamTemplate);
 | 
					            _logger.LogDebug("downstream template is {downstreamRoute.Data.ReRoute.DownstreamPath}", downstreamRoute.Data.ReRoute.DownstreamPathTemplate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            SetDownstreamRouteForThisRequest(downstreamRoute.Data);
 | 
					            SetDownstreamRouteForThisRequest(downstreamRoute.Data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					using Ocelot.Errors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.DownstreamUrlCreator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class DownstreamHostNullOrEmptyError : Error
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public DownstreamHostNullOrEmptyError()
 | 
				
			||||||
 | 
					            : base("downstream host was null or empty", OcelotErrorCode.DownstreamHostNullOrEmptyError)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					using Ocelot.Errors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.DownstreamUrlCreator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class DownstreamPathNullOrEmptyError : Error
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public DownstreamPathNullOrEmptyError() 
 | 
				
			||||||
 | 
					            : base("downstream path was null or empty", OcelotErrorCode.DownstreamPathNullOrEmptyError)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					using Ocelot.Errors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.DownstreamUrlCreator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class DownstreamSchemeNullOrEmptyError : Error
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public DownstreamSchemeNullOrEmptyError()
 | 
				
			||||||
 | 
					            : base("downstream scheme was null or empty", OcelotErrorCode.DownstreamSchemeNullOrEmptyError)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										10
									
								
								src/Ocelot/DownstreamUrlCreator/IUrlBuilder.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/Ocelot/DownstreamUrlCreator/IUrlBuilder.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					using Ocelot.Values;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.DownstreamUrlCreator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public interface IUrlBuilder
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Response<DownstreamUrl> Build(string downstreamPath, string downstreamScheme, HostAndPort downstreamHostAndPort);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,6 +1,5 @@
 | 
				
			|||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Microsoft.AspNetCore.Http;
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
using Microsoft.Extensions.Logging;
 | 
					 | 
				
			||||||
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
 | 
					using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
 | 
				
			||||||
using Ocelot.Infrastructure.RequestData;
 | 
					using Ocelot.Infrastructure.RequestData;
 | 
				
			||||||
using Ocelot.Logging;
 | 
					using Ocelot.Logging;
 | 
				
			||||||
@@ -11,17 +10,20 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
 | 
				
			|||||||
    public class DownstreamUrlCreatorMiddleware : OcelotMiddleware
 | 
					    public class DownstreamUrlCreatorMiddleware : OcelotMiddleware
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly RequestDelegate _next;
 | 
					        private readonly RequestDelegate _next;
 | 
				
			||||||
        private readonly IDownstreamUrlPathPlaceholderReplacer _urlReplacer;
 | 
					        private readonly IDownstreamPathPlaceholderReplacer _replacer;
 | 
				
			||||||
        private readonly IOcelotLogger _logger;
 | 
					        private readonly IOcelotLogger _logger;
 | 
				
			||||||
 | 
					        private readonly IUrlBuilder _urlBuilder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public DownstreamUrlCreatorMiddleware(RequestDelegate next,
 | 
					        public DownstreamUrlCreatorMiddleware(RequestDelegate next,
 | 
				
			||||||
            IOcelotLoggerFactory loggerFactory,
 | 
					            IOcelotLoggerFactory loggerFactory,
 | 
				
			||||||
            IDownstreamUrlPathPlaceholderReplacer urlReplacer,
 | 
					            IDownstreamPathPlaceholderReplacer replacer,
 | 
				
			||||||
            IRequestScopedDataRepository requestScopedDataRepository)
 | 
					            IRequestScopedDataRepository requestScopedDataRepository, 
 | 
				
			||||||
 | 
					            IUrlBuilder urlBuilder)
 | 
				
			||||||
            :base(requestScopedDataRepository)
 | 
					            :base(requestScopedDataRepository)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _next = next;
 | 
					            _next = next;
 | 
				
			||||||
            _urlReplacer = urlReplacer;
 | 
					            _replacer = replacer;
 | 
				
			||||||
 | 
					            _urlBuilder = urlBuilder;
 | 
				
			||||||
            _logger = loggerFactory.CreateLogger<DownstreamUrlCreatorMiddleware>();
 | 
					            _logger = loggerFactory.CreateLogger<DownstreamUrlCreatorMiddleware>();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -29,19 +31,34 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            _logger.LogDebug("started calling downstream url creator middleware");
 | 
					            _logger.LogDebug("started calling downstream url creator middleware");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var downstreamUrl = _urlReplacer.Replace(DownstreamRoute.ReRoute.DownstreamTemplate, DownstreamRoute.TemplatePlaceholderNameAndValues);
 | 
					            var dsPath = _replacer
 | 
				
			||||||
 | 
					                .Replace(DownstreamRoute.ReRoute.DownstreamPathTemplate, DownstreamRoute.TemplatePlaceholderNameAndValues);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (downstreamUrl.IsError)
 | 
					            if (dsPath.IsError)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _logger.LogDebug("IDownstreamUrlPathPlaceholderReplacer returned an error, setting pipeline error");
 | 
					                _logger.LogDebug("IDownstreamPathPlaceholderReplacer returned an error, setting pipeline error");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                SetPipelineError(downstreamUrl.Errors);
 | 
					                SetPipelineError(dsPath.Errors);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _logger.LogDebug("downstream url is {downstreamUrl.Data.Value}", downstreamUrl.Data.Value);
 | 
					            var dsScheme = DownstreamRoute.ReRoute.DownstreamScheme;
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            SetDownstreamUrlForThisRequest(downstreamUrl.Data.Value);
 | 
					            var dsHostAndPort = HostAndPort;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var dsUrl = _urlBuilder.Build(dsPath.Data.Value, dsScheme, dsHostAndPort);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (dsUrl.IsError)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                _logger.LogDebug("IUrlBuilder returned an error, setting pipeline error");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                SetPipelineError(dsUrl.Errors);
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _logger.LogDebug("downstream url is {downstreamUrl.Data.Value}", dsUrl.Data.Value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            SetDownstreamUrlForThisRequest(dsUrl.Data.Value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _logger.LogDebug("calling next middleware");
 | 
					            _logger.LogDebug("calling next middleware");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										45
									
								
								src/Ocelot/DownstreamUrlCreator/UrlBuilder.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/Ocelot/DownstreamUrlCreator/UrlBuilder.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using Ocelot.Errors;
 | 
				
			||||||
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					using Ocelot.Values;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.DownstreamUrlCreator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class UrlBuilder : IUrlBuilder
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public Response<DownstreamUrl> Build(string downstreamPath, string downstreamScheme, HostAndPort downstreamHostAndPort)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (string.IsNullOrEmpty(downstreamPath))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return new ErrorResponse<DownstreamUrl>(new List<Error> {new DownstreamPathNullOrEmptyError()});
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (string.IsNullOrEmpty(downstreamScheme))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return new ErrorResponse<DownstreamUrl>(new List<Error> { new DownstreamSchemeNullOrEmptyError() });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (string.IsNullOrEmpty(downstreamHostAndPort.DownstreamHost))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return new ErrorResponse<DownstreamUrl>(new List<Error> { new DownstreamHostNullOrEmptyError() });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var builder = new UriBuilder
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Host = downstreamHostAndPort.DownstreamHost,
 | 
				
			||||||
 | 
					                Path = downstreamPath,
 | 
				
			||||||
 | 
					                Scheme = downstreamScheme
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (downstreamHostAndPort.DownstreamPort > 0)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                builder.Port = downstreamHostAndPort.DownstreamPort;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            var url = builder.Uri.ToString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return new OkResponse<DownstreamUrl>(new DownstreamUrl(url));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,25 +1,25 @@
 | 
				
			|||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.Text;
 | 
					using System.Text;
 | 
				
			||||||
using Ocelot.DownstreamRouteFinder;
 | 
					 | 
				
			||||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
 | 
					using Ocelot.DownstreamRouteFinder.UrlMatcher;
 | 
				
			||||||
using Ocelot.Responses;
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					using Ocelot.Values;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer
 | 
					namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class DownstreamUrlPathPlaceholderReplacer : IDownstreamUrlPathPlaceholderReplacer
 | 
					    public class DownstreamTemplatePathPlaceholderReplacer : IDownstreamPathPlaceholderReplacer
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public Response<DownstreamUrl> Replace(string downstreamTemplate, List<UrlPathPlaceholderNameAndValue> urlPathPlaceholderNameAndValues)
 | 
					        public Response<DownstreamPath> Replace(PathTemplate downstreamPathTemplate, List<UrlPathPlaceholderNameAndValue> urlPathPlaceholderNameAndValues)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var upstreamUrl = new StringBuilder();
 | 
					            var downstreamPath = new StringBuilder();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            upstreamUrl.Append(downstreamTemplate);
 | 
					            downstreamPath.Append(downstreamPathTemplate.Value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            foreach (var placeholderVariableAndValue in urlPathPlaceholderNameAndValues)
 | 
					            foreach (var placeholderVariableAndValue in urlPathPlaceholderNameAndValues)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                upstreamUrl.Replace(placeholderVariableAndValue.TemplateVariableName, placeholderVariableAndValue.TemplateVariableValue);
 | 
					                downstreamPath.Replace(placeholderVariableAndValue.TemplateVariableName, placeholderVariableAndValue.TemplateVariableValue);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return new OkResponse<DownstreamUrl>(new DownstreamUrl(upstreamUrl.ToString()));
 | 
					            return new OkResponse<DownstreamPath>(new DownstreamPath(downstreamPath.ToString()));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,11 +1,12 @@
 | 
				
			|||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
 | 
					using Ocelot.DownstreamRouteFinder.UrlMatcher;
 | 
				
			||||||
using Ocelot.Responses;
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					using Ocelot.Values;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer
 | 
					namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public interface IDownstreamUrlPathPlaceholderReplacer
 | 
					    public interface IDownstreamPathPlaceholderReplacer
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Response<DownstreamUrl> Replace(string downstreamTemplate, List<UrlPathPlaceholderNameAndValue> urlPathPlaceholderNameAndValues);   
 | 
					        Response<DownstreamPath> Replace(PathTemplate downstreamPathTemplate, List<UrlPathPlaceholderNameAndValue> urlPathPlaceholderNameAndValues);   
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,8 +1,6 @@
 | 
				
			|||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.IO;
 | 
					 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Microsoft.AspNetCore.Http;
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
using Microsoft.Extensions.Logging;
 | 
					 | 
				
			||||||
using Ocelot.Infrastructure.RequestData;
 | 
					using Ocelot.Infrastructure.RequestData;
 | 
				
			||||||
using Ocelot.Logging;
 | 
					using Ocelot.Logging;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -41,13 +39,13 @@ namespace Ocelot.Errors.Middleware
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                var message = CreateMessage(context, e);
 | 
					                var message = CreateMessage(context, e);
 | 
				
			||||||
                _logger.LogError(message, e);
 | 
					                _logger.LogError(message, e);
 | 
				
			||||||
                await SetInternalServerErrorOnResponse(context);
 | 
					                SetInternalServerErrorOnResponse(context);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _logger.LogDebug("ocelot pipeline finished");
 | 
					            _logger.LogDebug("ocelot pipeline finished");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private async Task SetInternalServerErrorOnResponse(HttpContext context)
 | 
					        private void SetInternalServerErrorOnResponse(HttpContext context)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            context.Response.OnStarting(x =>
 | 
					            context.Response.OnStarting(x =>
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        UnauthenticatedError, 
 | 
					        UnauthenticatedError, 
 | 
				
			||||||
        UnknownError,
 | 
					        UnknownError,
 | 
				
			||||||
        DownstreamTemplateAlreadyUsedError,
 | 
					        DownstreampathTemplateAlreadyUsedError,
 | 
				
			||||||
        UnableToFindDownstreamRouteError,
 | 
					        UnableToFindDownstreamRouteError,
 | 
				
			||||||
        CannotAddDataError,
 | 
					        CannotAddDataError,
 | 
				
			||||||
        CannotFindDataError,
 | 
					        CannotFindDataError,
 | 
				
			||||||
@@ -17,6 +17,16 @@
 | 
				
			|||||||
        InstructionNotForClaimsError,
 | 
					        InstructionNotForClaimsError,
 | 
				
			||||||
        UnauthorizedError,
 | 
					        UnauthorizedError,
 | 
				
			||||||
        ClaimValueNotAuthorisedError,
 | 
					        ClaimValueNotAuthorisedError,
 | 
				
			||||||
        UserDoesNotHaveClaimError
 | 
					        UserDoesNotHaveClaimError,
 | 
				
			||||||
 | 
					        DownstreamPathTemplateContainsSchemeError,
 | 
				
			||||||
 | 
					        DownstreamPathNullOrEmptyError,
 | 
				
			||||||
 | 
					        DownstreamSchemeNullOrEmptyError,
 | 
				
			||||||
 | 
					        DownstreamHostNullOrEmptyError,
 | 
				
			||||||
 | 
					        ServicesAreNullError,
 | 
				
			||||||
 | 
					        ServicesAreEmptyError,
 | 
				
			||||||
 | 
					        UnableToFindServiceDiscoveryProviderError,
 | 
				
			||||||
 | 
					        UnableToFindLoadBalancerError,
 | 
				
			||||||
 | 
					        RequestTimedOutError,
 | 
				
			||||||
 | 
					        UnableToFindQoSProviderError
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,6 @@
 | 
				
			|||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Microsoft.AspNetCore.Http;
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
using Microsoft.Extensions.Logging;
 | 
					 | 
				
			||||||
using Ocelot.Infrastructure.RequestData;
 | 
					using Ocelot.Infrastructure.RequestData;
 | 
				
			||||||
using Ocelot.Logging;
 | 
					using Ocelot.Logging;
 | 
				
			||||||
using Ocelot.Middleware;
 | 
					using Ocelot.Middleware;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,8 +3,6 @@ using Ocelot.Responses;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.Headers
 | 
					namespace Ocelot.Headers
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    using System;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public class RemoveOutputHeaders : IRemoveOutputHeaders
 | 
					    public class RemoveOutputHeaders : IRemoveOutputHeaders
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										24
									
								
								src/Ocelot/Infrastructure/Extensions/StringExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/Ocelot/Infrastructure/Extensions/StringExtensions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Infrastructure.Extensions
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public static class StringExtensions
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public static string TrimStart(this string source, string trim, StringComparison stringComparison = StringComparison.Ordinal)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (source == null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return null;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            string s = source;
 | 
				
			||||||
 | 
					            while (s.StartsWith(trim, stringComparison))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                s = s.Substring(trim.Length);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return s;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										12
									
								
								src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancer.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancer.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					using Ocelot.Values;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.LoadBalancer.LoadBalancers
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public interface ILoadBalancer
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Task<Response<HostAndPort>> Lease();
 | 
				
			||||||
 | 
					        void Release(HostAndPort hostAndPort);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Ocelot.Configuration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.LoadBalancer.LoadBalancers
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public interface ILoadBalancerFactory
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Task<ILoadBalancer> Get(ReRoute reRoute);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										10
									
								
								src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerHouse.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerHouse.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.LoadBalancer.LoadBalancers
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public interface ILoadBalancerHouse
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Response<ILoadBalancer> Get(string key);
 | 
				
			||||||
 | 
					        Response Add(string key, ILoadBalancer loadBalancer);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										15
									
								
								src/Ocelot/LoadBalancer/LoadBalancers/Lease.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/Ocelot/LoadBalancer/LoadBalancers/Lease.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					using Ocelot.Values;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.LoadBalancer.LoadBalancers
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class Lease
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public Lease(HostAndPort hostAndPort, int connections)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            HostAndPort = hostAndPort;
 | 
				
			||||||
 | 
					            Connections = connections;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        public HostAndPort HostAndPort { get; private set; }
 | 
				
			||||||
 | 
					        public int Connections { get; private set; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,145 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Ocelot.Errors;
 | 
				
			||||||
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					using Ocelot.Values;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.LoadBalancer.LoadBalancers
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class LeastConnectionLoadBalancer : ILoadBalancer
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private readonly Func<Task<List<Service>>> _services;
 | 
				
			||||||
 | 
					        private readonly List<Lease> _leases;
 | 
				
			||||||
 | 
					        private readonly string _serviceName;
 | 
				
			||||||
 | 
					        private static readonly object _syncLock = new object();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public LeastConnectionLoadBalancer(Func<Task<List<Service>>> services, string serviceName)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _services = services;
 | 
				
			||||||
 | 
					            _serviceName = serviceName;
 | 
				
			||||||
 | 
					            _leases = new List<Lease>();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task<Response<HostAndPort>> Lease()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var services = await _services.Invoke();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (services == null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return new ErrorResponse<HostAndPort>(new List<Error>() { new ServicesAreNullError($"services were null for {_serviceName}") });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!services.Any())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return new ErrorResponse<HostAndPort>(new List<Error>() { new ServicesAreEmptyError($"services were empty for {_serviceName}") });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            lock(_syncLock)
 | 
				
			||||||
 | 
					            {        
 | 
				
			||||||
 | 
					                //todo - maybe this should be moved somewhere else...? Maybe on a repeater on seperate thread? loop every second and update or something?
 | 
				
			||||||
 | 
					                UpdateServices(services);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                var leaseWithLeastConnections = GetLeaseWithLeastConnections();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                _leases.Remove(leaseWithLeastConnections);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                leaseWithLeastConnections = AddConnection(leaseWithLeastConnections);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                _leases.Add(leaseWithLeastConnections);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					                return new OkResponse<HostAndPort>(new HostAndPort(leaseWithLeastConnections.HostAndPort.DownstreamHost, leaseWithLeastConnections.HostAndPort.DownstreamPort));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void Release(HostAndPort hostAndPort)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            lock(_syncLock)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var matchingLease = _leases.FirstOrDefault(l => l.HostAndPort.DownstreamHost == hostAndPort.DownstreamHost
 | 
				
			||||||
 | 
					                    && l.HostAndPort.DownstreamPort == hostAndPort.DownstreamPort);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (matchingLease != null)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    var replacementLease = new Lease(hostAndPort, matchingLease.Connections - 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    _leases.Remove(matchingLease);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    _leases.Add(replacementLease);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private Lease AddConnection(Lease lease)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return new Lease(lease.HostAndPort, lease.Connections + 1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private Lease GetLeaseWithLeastConnections()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            //now get the service with the least connections?
 | 
				
			||||||
 | 
					            Lease leaseWithLeastConnections = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (var i = 0; i < _leases.Count; i++)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (i == 0)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    leaseWithLeastConnections = _leases[i];
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    if (_leases[i].Connections < leaseWithLeastConnections.Connections)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        leaseWithLeastConnections = _leases[i];
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return leaseWithLeastConnections;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private Response UpdateServices(List<Service> services)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (_leases.Count > 0)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var leasesToRemove = new List<Lease>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                foreach (var lease in _leases)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    var match = services.FirstOrDefault(s => s.HostAndPort.DownstreamHost == lease.HostAndPort.DownstreamHost
 | 
				
			||||||
 | 
					                        && s.HostAndPort.DownstreamPort == lease.HostAndPort.DownstreamPort);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (match == null)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        leasesToRemove.Add(lease);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                foreach (var lease in leasesToRemove)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    _leases.Remove(lease);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                foreach (var service in services)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    var exists = _leases.FirstOrDefault(l => l.HostAndPort.ToString() == service.HostAndPort.ToString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (exists == null)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        _leases.Add(new Lease(service.HostAndPort, 0));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                foreach (var service in services)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    _leases.Add(new Lease(service.HostAndPort, 0));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return new OkResponse();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										30
									
								
								src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Ocelot.Configuration;
 | 
				
			||||||
 | 
					using Ocelot.ServiceDiscovery;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.LoadBalancer.LoadBalancers
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class LoadBalancerFactory : ILoadBalancerFactory
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private readonly IServiceDiscoveryProviderFactory _serviceProviderFactory;
 | 
				
			||||||
 | 
					        public LoadBalancerFactory(IServiceDiscoveryProviderFactory serviceProviderFactory)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _serviceProviderFactory = serviceProviderFactory;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task<ILoadBalancer> Get(ReRoute reRoute)
 | 
				
			||||||
 | 
					        {            
 | 
				
			||||||
 | 
					            var serviceProvider = _serviceProviderFactory.Get(reRoute.ServiceProviderConfiguraion);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            switch (reRoute.LoadBalancer)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                case "RoundRobin":
 | 
				
			||||||
 | 
					                    return new RoundRobinLoadBalancer(await serviceProvider.Get());
 | 
				
			||||||
 | 
					                case "LeastConnection":
 | 
				
			||||||
 | 
					                    return new LeastConnectionLoadBalancer(async () => await serviceProvider.Get(), reRoute.ServiceProviderConfiguraion.ServiceName);
 | 
				
			||||||
 | 
					                default:
 | 
				
			||||||
 | 
					                    return new NoLoadBalancer(await serviceProvider.Get());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										42
									
								
								src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerHouse.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerHouse.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.LoadBalancer.LoadBalancers
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class LoadBalancerHouse : ILoadBalancerHouse
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private readonly Dictionary<string, ILoadBalancer> _loadBalancers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public LoadBalancerHouse()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _loadBalancers = new Dictionary<string, ILoadBalancer>();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public Response<ILoadBalancer> Get(string key)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ILoadBalancer loadBalancer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if(_loadBalancers.TryGetValue(key, out loadBalancer))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return new OkResponse<ILoadBalancer>(_loadBalancers[key]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return new ErrorResponse<ILoadBalancer>(new List<Ocelot.Errors.Error>()
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                new UnableToFindLoadBalancerError($"unabe to find load balancer for {key}")
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public Response Add(string key, ILoadBalancer loadBalancer)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (!_loadBalancers.ContainsKey(key))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                _loadBalancers.Add(key, loadBalancer);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _loadBalancers.Remove(key);
 | 
				
			||||||
 | 
					            _loadBalancers.Add(key, loadBalancer);
 | 
				
			||||||
 | 
					            return new OkResponse();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										28
									
								
								src/Ocelot/LoadBalancer/LoadBalancers/NoLoadBalancer.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/Ocelot/LoadBalancer/LoadBalancers/NoLoadBalancer.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					using Ocelot.Values;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.LoadBalancer.LoadBalancers
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class NoLoadBalancer : ILoadBalancer
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private readonly List<Service> _services;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public NoLoadBalancer(List<Service> services)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _services = services;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task<Response<HostAndPort>> Lease()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var service = await Task.FromResult(_services.FirstOrDefault());
 | 
				
			||||||
 | 
					            return new OkResponse<HostAndPort>(service.HostAndPort);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void Release(HostAndPort hostAndPort)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					using Ocelot.Values;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.LoadBalancer.LoadBalancers
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class RoundRobinLoadBalancer : ILoadBalancer
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private readonly List<Service> _services;
 | 
				
			||||||
 | 
					        private int _last;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public RoundRobinLoadBalancer(List<Service> services)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _services = services;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task<Response<HostAndPort>> Lease()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (_last >= _services.Count)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                _last = 0;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var next = await Task.FromResult(_services[_last]);
 | 
				
			||||||
 | 
					            _last++;
 | 
				
			||||||
 | 
					            return new OkResponse<HostAndPort>(next.HostAndPort);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void Release(HostAndPort hostAndPort)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					using Ocelot.Errors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.LoadBalancer.LoadBalancers
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class ServicesAreEmptyError : Error
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public ServicesAreEmptyError(string message)
 | 
				
			||||||
 | 
					            : base(message, OcelotErrorCode.ServicesAreEmptyError)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user