mirror of
				https://github.com/nsnail/Ocelot.git
				synced 2025-11-04 18:00:48 +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/
 | 
			
		||||
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}"
 | 
			
		||||
	ProjectSection(SolutionItems) = preProject
 | 
			
		||||
		.gitignore = .gitignore
 | 
			
		||||
		appveyor.yml = appveyor.yml
 | 
			
		||||
		build-and-run-tests.bat = build-and-run-tests.bat
 | 
			
		||||
		build.bat = build.bat
 | 
			
		||||
		build-and-release-unstable.ps1 = build-and-release-unstable.ps1
 | 
			
		||||
		build-and-run-tests.ps1 = build-and-run-tests.ps1
 | 
			
		||||
		build.cake = build.cake
 | 
			
		||||
		build.ps1 = build.ps1
 | 
			
		||||
		build.readme.md = build.readme.md
 | 
			
		||||
		configuration-explanation.txt = configuration-explanation.txt
 | 
			
		||||
		configuration.yaml = configuration.yaml
 | 
			
		||||
		GitVersion.yml = GitVersion.yml
 | 
			
		||||
		global.json = global.json
 | 
			
		||||
		LICENSE.md = LICENSE.md
 | 
			
		||||
		Ocelot.nuspec = Ocelot.nuspec
 | 
			
		||||
		push-to-nuget.bat = push-to-nuget.bat
 | 
			
		||||
		ocelot.postman_collection.json = ocelot.postman_collection.json
 | 
			
		||||
		README.md = README.md
 | 
			
		||||
		run-benchmarks.bat = run-benchmarks.bat
 | 
			
		||||
		run-tests.bat = run-tests.bat
 | 
			
		||||
		release.ps1 = release.ps1
 | 
			
		||||
		ReleaseNotes.md = ReleaseNotes.md
 | 
			
		||||
		run-acceptance-tests.ps1 = run-acceptance-tests.ps1
 | 
			
		||||
		run-benchmarks.ps1 = run-benchmarks.ps1
 | 
			
		||||
		run-unit-tests.ps1 = run-unit-tests.ps1
 | 
			
		||||
		version.ps1 = version.ps1
 | 
			
		||||
	EndProjectSection
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{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
 | 
			
		||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Ocelot.Benchmarks", "test\Ocelot.Benchmarks\Ocelot.Benchmarks.xproj", "{106B49E6-95F6-4A7B-B81C-96BFA74AF035}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Ocelot.IntegrationTests", "test\Ocelot.IntegrationTests\Ocelot.IntegrationTests.xproj", "{D4575572-99CA-4530-8737-C296EDA326F8}"
 | 
			
		||||
EndProject
 | 
			
		||||
Global
 | 
			
		||||
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 | 
			
		||||
		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}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{D4575572-99CA-4530-8737-C296EDA326F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{D4575572-99CA-4530-8737-C296EDA326F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{D4575572-99CA-4530-8737-C296EDA326F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{D4575572-99CA-4530-8737-C296EDA326F8}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
	EndGlobalSection
 | 
			
		||||
	GlobalSection(SolutionProperties) = preSolution
 | 
			
		||||
		HideSolutionNode = FALSE
 | 
			
		||||
@@ -69,5 +82,6 @@ Global
 | 
			
		||||
		{F8C224FE-36BE-45F5-9B0E-666D8F4A9B52} = {5B401523-36DA-4491-B73A-7590A26E420B}
 | 
			
		||||
		{02BBF4C5-517E-4157-8D21-4B8B9E118B7A} = {5B401523-36DA-4491-B73A-7590A26E420B}
 | 
			
		||||
		{106B49E6-95F6-4A7B-B81C-96BFA74AF035} = {5B401523-36DA-4491-B73A-7590A26E420B}
 | 
			
		||||
		{D4575572-99CA-4530-8737-C296EDA326F8} = {5B401523-36DA-4491-B73A-7590A26E420B}
 | 
			
		||||
	EndGlobalSection
 | 
			
		||||
EndGlobal
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										354
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										354
									
								
								README.md
									
									
									
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
# 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)
 | 
			
		||||
 | 
			
		||||
@@ -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 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
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
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 
 | 
			
		||||
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 -Pre`
 | 
			
		||||
`Install-Package 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
 | 
			
		||||
if you don't want to manage lots of ReRoute specific settings.
 | 
			
		||||
 | 
			
		||||
		{
 | 
			
		||||
			"ReRoutes": [],
 | 
			
		||||
			"GlobalConfiguration": {}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
    "ReRoutes": [],
 | 
			
		||||
    "GlobalConfiguration": {}
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
More information on how to use these options is below..
 | 
			
		||||
 | 
			
		||||
## Startup
 | 
			
		||||
@@ -73,46 +71,75 @@ More information on how to use these options is 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.
 | 
			
		||||
 | 
			
		||||
	 public class Startup
 | 
			
		||||
```csharp
 | 
			
		||||
public class Startup
 | 
			
		||||
{
 | 
			
		||||
    public Startup(IHostingEnvironment env)
 | 
			
		||||
    {
 | 
			
		||||
        public Startup(IHostingEnvironment env)
 | 
			
		||||
        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) =>
 | 
			
		||||
        {
 | 
			
		||||
            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 =>
 | 
			
		||||
            {
 | 
			
		||||
                x.WithMicrosoftLogging(log =>
 | 
			
		||||
                {
 | 
			
		||||
                    log.AddConsole(LogLevel.Debug);
 | 
			
		||||
                })
 | 
			
		||||
                .WithDictionaryHandle();
 | 
			
		||||
            };
 | 
			
		||||
                log.AddConsole(LogLevel.Debug);
 | 
			
		||||
            })
 | 
			
		||||
            .WithDictionaryHandle();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
            services.AddOcelotOutputCaching(settings);
 | 
			
		||||
            services.AddOcelotFileConfiguration(Configuration);
 | 
			
		||||
            services.AddOcelot();
 | 
			
		||||
            services.AddOcelot(Configuration);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
 | 
			
		||||
        public async void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
 | 
			
		||||
        {
 | 
			
		||||
            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! 
 | 
			
		||||
 | 
			
		||||
@@ -122,59 +149,151 @@ 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
 | 
			
		||||
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 
 | 
			
		||||
anything working in Ocelot you need to set up a ReRoute in the configuration.
 | 
			
		||||
 | 
			
		||||
		{
 | 
			
		||||
			"ReRoutes": [
 | 
			
		||||
			]
 | 
			
		||||
		}
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
    "ReRoutes": [
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
In order to set up a ReRoute you need to add one to the json array called ReRoutes like
 | 
			
		||||
the following.
 | 
			
		||||
 | 
			
		||||
		{
 | 
			
		||||
            "DownstreamTemplate": "http://jsonplaceholder.typicode.com/posts/{postId}",
 | 
			
		||||
            "UpstreamTemplate": "/posts/{postId}",
 | 
			
		||||
            "UpstreamHttpMethod": "Put"
 | 
			
		||||
        }
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
    "DownstreamPathTemplate": "/api/posts/{postId}",
 | 
			
		||||
    "DownstreamScheme": "https",
 | 
			
		||||
    "DownstreamPort": 80,
 | 
			
		||||
    "DownstreamHost" "localhost"
 | 
			
		||||
    "UpstreamPathTemplate": "/posts/{postId}",
 | 
			
		||||
    "UpstreamHttpMethod": "Put"
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The DownstreamTemplate is the URL that this request will be forwarded to.
 | 
			
		||||
The UpstreamTemplate is the URL that Ocelot will use to identity which 
 | 
			
		||||
DownstreamTemplate to use for a given request. Finally the UpstreamHttpMethod is used so
 | 
			
		||||
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 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 
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
		"ReRouteIsCaseSensitive": true
 | 
			
		||||
```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.
 | 
			
		||||
 | 
			
		||||
```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
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
		"AuthenticationOptions": {
 | 
			
		||||
            "Provider": "IdentityServer",
 | 
			
		||||
            "ProviderRootUrl": "http://localhost:52888",
 | 
			
		||||
            "ScopeName": "api",
 | 
			
		||||
            "AdditionalScopes": [
 | 
			
		||||
                "openid",
 | 
			
		||||
                "offline_access"
 | 
			
		||||
            ],
 | 
			
		||||
            "ScopeSecret": "secret"
 | 
			
		||||
        }
 | 
			
		||||
```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
 | 
			
		||||
@@ -193,9 +312,11 @@ is 401 unauthorised.
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
		"RouteClaimsRequirement": {
 | 
			
		||||
            "UserType": "registered"
 | 
			
		||||
        },
 | 
			
		||||
```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. 
 | 
			
		||||
@@ -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
 | 
			
		||||
 | 
			
		||||
		 "AddClaimsToRequest": {
 | 
			
		||||
			"UserType": "Claims[sub] > value[0] > |",
 | 
			
		||||
			"UserId": "Claims[sub] > value[1] > |"
 | 
			
		||||
		},
 | 
			
		||||
```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".
 | 
			
		||||
@@ -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
 | 
			
		||||
 | 
			
		||||
	   "AddHeadersToRequest": {
 | 
			
		||||
			"CustomerId": "Claims[sub] > value[1] > |"
 | 
			
		||||
		},
 | 
			
		||||
```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".
 | 
			
		||||
@@ -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
 | 
			
		||||
 | 
			
		||||
		"AddQueriesToRequest": {
 | 
			
		||||
            "LocationId": "Claims[LocationId] > value",
 | 
			
		||||
        },
 | 
			
		||||
```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.
 | 
			
		||||
 | 
			
		||||
## Logging
 | 
			
		||||
## Quality of Service
 | 
			
		||||
 | 
			
		||||
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. 
 | 
			
		||||
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).
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
Add the following section to a ReRoute configuration. 
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
```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
 | 
			
		||||
 | 
			
		||||
@@ -291,14 +426,18 @@ have an OcelotRequestId.
 | 
			
		||||
 | 
			
		||||
In order to use the requestid feature in your ReRoute configuration add this setting
 | 
			
		||||
 | 
			
		||||
		"RequestIdKey": "OcRequestId"
 | 
			
		||||
```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.
 | 
			
		||||
 | 
			
		||||
		"RequestIdKey": "OcRequestId",
 | 
			
		||||
```json
 | 
			
		||||
"RequestIdKey": "OcRequestId",
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
		"FileCacheOptions": { "TtlSeconds": 15 }
 | 
			
		||||
```json
 | 
			
		||||
"FileCacheOptions": { "TtlSeconds": 15 }
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
In this example ttl seconds is set to 15 which means the cache will expire after 15 seconds.
 | 
			
		||||
 | 
			
		||||
@@ -329,15 +470,17 @@ 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.
 | 
			
		||||
 | 
			
		||||
		var configuration = new OcelotMiddlewareConfiguration
 | 
			
		||||
		{
 | 
			
		||||
			PreErrorResponderMiddleware = async (ctx, next) =>
 | 
			
		||||
			{
 | 
			
		||||
				await next.Invoke();
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
```csharp
 | 
			
		||||
var configuration = new OcelotMiddlewareConfiguration
 | 
			
		||||
{
 | 
			
		||||
    PreErrorResponderMiddleware = async (ctx, next) =>
 | 
			
		||||
    {
 | 
			
		||||
        await next.Invoke();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
		app.UseOcelot(configuration);
 | 
			
		||||
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.
 | 
			
		||||
@@ -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
 | 
			
		||||
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...
 | 
			
		||||
@@ -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
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
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": [
 | 
			
		||||
		{
 | 
			
		||||
			# The url we are forwarding the request to, ocelot will not add a trailing slash
 | 
			
		||||
			"DownstreamTemplate": "http://somehost.com/identityserverexample",
 | 
			
		||||
			# The path 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
 | 
			
		||||
			# to that so everything matches
 | 
			
		||||
			"UpstreamTemplate": "/identityserverexample",
 | 
			
		||||
			# The downstream path we are forwarding the request to, ocelot will not add a trailing slash.
 | 
			
		||||
			# Ocelot replaces any placeholders {etc} with matched values from the incoming request.
 | 
			
		||||
			"DownstreamPathTemplate": "/identityserverexample/{someid}/something",
 | 
			
		||||
			# The scheme you want Ocelot to use when making the downstream request
 | 
			
		||||
			"DownstreamScheme": "https",
 | 
			
		||||
			# 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
 | 
			
		||||
			"UpstreamHttpMethod": "Get",
 | 
			
		||||
			# Only support identity server at the moment
 | 
			
		||||
@@ -71,12 +80,24 @@
 | 
			
		||||
			# the caching a lot.
 | 
			
		||||
			"FileCacheOptions": { "TtlSeconds": 15 },
 | 
			
		||||
			# 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
 | 
			
		||||
		"GlobalConfiguration": {
 | 
			
		||||
			# If this is set it will override any route specific request id keys, behaves the same
 | 
			
		||||
			# otherwise
 | 
			
		||||
			"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>
 | 
			
		||||
    public class AuthenticationHandlerCreator : IAuthenticationHandlerCreator
 | 
			
		||||
    {
 | 
			
		||||
        public Response<RequestDelegate> CreateIdentityServerAuthenticationHandler(IApplicationBuilder app, AuthenticationOptions authOptions)
 | 
			
		||||
        public Response<RequestDelegate> Create(IApplicationBuilder app, AuthenticationOptions authOptions)
 | 
			
		||||
        {
 | 
			
		||||
            var builder = app.New();
 | 
			
		||||
 | 
			
		||||
            builder.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
 | 
			
		||||
            {
 | 
			
		||||
                Authority = authOptions.ProviderRootUrl,
 | 
			
		||||
                ScopeName = authOptions.ScopeName,
 | 
			
		||||
                ApiName = authOptions.ScopeName,
 | 
			
		||||
                RequireHttpsMetadata = authOptions.RequireHttps,
 | 
			
		||||
                AdditionalScopes = authOptions.AdditionalScopes,
 | 
			
		||||
                AllowedScopes = authOptions.AdditionalScopes,
 | 
			
		||||
                SupportedTokens = SupportedTokens.Both,
 | 
			
		||||
                ScopeSecret = authOptions.ScopeSecret
 | 
			
		||||
                ApiSecret = authOptions.ScopeSecret
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            var authenticationNext = builder.Build();
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,6 @@ namespace Ocelot.Authentication.Handler.Creator
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
        {
 | 
			
		||||
            var handler = _creator.CreateIdentityServerAuthenticationHandler(app, authOptions);
 | 
			
		||||
            var handler = _creator.Create(app, authOptions);
 | 
			
		||||
 | 
			
		||||
            if (!handler.IsError)
 | 
			
		||||
            {
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Microsoft.AspNetCore.Builder;
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
using Microsoft.Extensions.Logging;
 | 
			
		||||
using Ocelot.Authentication.Handler.Factory;
 | 
			
		||||
using Ocelot.Configuration;
 | 
			
		||||
using Ocelot.Errors;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Security.Claims;
 | 
			
		||||
using Ocelot.Errors;
 | 
			
		||||
using Ocelot.Responses;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
using Microsoft.Extensions.Logging;
 | 
			
		||||
using Ocelot.Infrastructure.RequestData;
 | 
			
		||||
using Ocelot.Infrastructure.RequestData;
 | 
			
		||||
using Ocelot.Logging;
 | 
			
		||||
using Ocelot.Responses;
 | 
			
		||||
 | 
			
		||||
@@ -61,7 +60,7 @@ namespace Ocelot.Authorisation.Middleware
 | 
			
		||||
                    SetPipelineError(new List<Error>
 | 
			
		||||
                    {
 | 
			
		||||
                        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.Threading.Tasks;
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
using Microsoft.Extensions.Logging;
 | 
			
		||||
using Ocelot.Infrastructure.RequestData;
 | 
			
		||||
using Ocelot.Logging;
 | 
			
		||||
using Ocelot.Middleware;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
using Microsoft.Extensions.Logging;
 | 
			
		||||
using Ocelot.Infrastructure.RequestData;
 | 
			
		||||
using Ocelot.Logging;
 | 
			
		||||
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.Net.Http;
 | 
			
		||||
using Ocelot.Values;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Configuration.Builder
 | 
			
		||||
{
 | 
			
		||||
    public class ReRouteBuilder
 | 
			
		||||
    {
 | 
			
		||||
        private string _downstreamTemplate;
 | 
			
		||||
        private AuthenticationOptions _authenticationOptions;
 | 
			
		||||
        private string _loadBalancerKey;
 | 
			
		||||
        private string _downstreamPathTemplate;
 | 
			
		||||
        private string _upstreamTemplate;
 | 
			
		||||
        private string _upstreamTemplatePattern;
 | 
			
		||||
        private string _upstreamHttpMethod;
 | 
			
		||||
        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> _claimToClaims;
 | 
			
		||||
        private Dictionary<string, string> _routeClaimRequirement;
 | 
			
		||||
@@ -23,19 +21,41 @@ namespace Ocelot.Configuration.Builder
 | 
			
		||||
        private string _requestIdHeaderKey;
 | 
			
		||||
        private bool _isCached;
 | 
			
		||||
        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>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ReRouteBuilder WithDownstreamTemplate(string input)
 | 
			
		||||
        {
 | 
			
		||||
            _downstreamTemplate = input;
 | 
			
		||||
          _loadBalancer = loadBalancer;
 | 
			
		||||
            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;
 | 
			
		||||
            return this;
 | 
			
		||||
@@ -63,42 +83,6 @@ namespace Ocelot.Configuration.Builder
 | 
			
		||||
            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)
 | 
			
		||||
        {
 | 
			
		||||
            _requestIdHeaderKey = input;
 | 
			
		||||
@@ -141,12 +125,83 @@ namespace Ocelot.Configuration.Builder
 | 
			
		||||
            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()
 | 
			
		||||
        {
 | 
			
		||||
            return new ReRoute(_downstreamTemplate, _upstreamTemplate, _upstreamHttpMethod, _upstreamTemplatePattern, 
 | 
			
		||||
                _isAuthenticated, new AuthenticationOptions(_authenticationProvider, _authenticationProviderUrl, _scopeName, 
 | 
			
		||||
                _requireHttps, _additionalScopes, _scopeSecret), _configHeaderExtractorProperties, _claimToClaims, _routeClaimRequirement, 
 | 
			
		||||
                _isAuthorised, _claimToQueries, _requestIdHeaderKey, _isCached, _fileCacheOptions);
 | 
			
		||||
            return new ReRoute(
 | 
			
		||||
                new PathTemplate(_downstreamPathTemplate), 
 | 
			
		||||
                new PathTemplate(_upstreamTemplate), 
 | 
			
		||||
                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.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Microsoft.Extensions.Logging;
 | 
			
		||||
using Microsoft.Extensions.Options;
 | 
			
		||||
using Ocelot.Configuration.Builder;
 | 
			
		||||
using Ocelot.Configuration.File;
 | 
			
		||||
using Ocelot.Configuration.Parser;
 | 
			
		||||
using Ocelot.Configuration.Validator;
 | 
			
		||||
using Ocelot.LoadBalancer.LoadBalancers;
 | 
			
		||||
using Ocelot.Requester.QoS;
 | 
			
		||||
using Ocelot.Responses;
 | 
			
		||||
using Ocelot.Utilities;
 | 
			
		||||
 | 
			
		||||
@@ -21,36 +25,52 @@ namespace Ocelot.Configuration.Creator
 | 
			
		||||
        private const string RegExMatchEverything = ".*";
 | 
			
		||||
        private const string RegExMatchEndString = "$";
 | 
			
		||||
        private const string RegExIgnoreCase = "(?i)";
 | 
			
		||||
        private const string RegExForwardSlashOnly = "^/$";
 | 
			
		||||
 | 
			
		||||
        private readonly IClaimToThingConfigurationParser _claimToThingConfigurationParser;
 | 
			
		||||
        private readonly ILogger<FileOcelotConfigurationCreator> _logger;
 | 
			
		||||
        private readonly ILoadBalancerFactory _loadBalanceFactory;
 | 
			
		||||
        private readonly ILoadBalancerHouse _loadBalancerHouse;
 | 
			
		||||
        private readonly IQoSProviderFactory _qoSProviderFactory;
 | 
			
		||||
        private readonly IQosProviderHouse _qosProviderHouse;
 | 
			
		||||
 | 
			
		||||
        public FileOcelotConfigurationCreator(
 | 
			
		||||
            IOptions<FileConfiguration> options, 
 | 
			
		||||
            IConfigurationValidator configurationValidator, 
 | 
			
		||||
            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;
 | 
			
		||||
            _configurationValidator = configurationValidator;
 | 
			
		||||
            _claimToThingConfigurationParser = claimToThingConfigurationParser;
 | 
			
		||||
            _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);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 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()
 | 
			
		||||
        public async Task<Response<IOcelotConfiguration>> Create(FileConfiguration fileConfiguration)
 | 
			
		||||
        {     
 | 
			
		||||
            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)
 | 
			
		||||
            {
 | 
			
		||||
@@ -66,59 +86,190 @@ namespace Ocelot.Configuration.Creator
 | 
			
		||||
 | 
			
		||||
            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);
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            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 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
 | 
			
		||||
                : reRoute.RequestIdKey;
 | 
			
		||||
                : fileReRoute.RequestIdKey;
 | 
			
		||||
 | 
			
		||||
            if (isAuthenticated)
 | 
			
		||||
            {
 | 
			
		||||
                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, 
 | 
			
		||||
                reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated, 
 | 
			
		||||
                null, new List<ClaimToThing>(), new List<ClaimToThing>(), 
 | 
			
		||||
                reRoute.RouteClaimsRequirement, isAuthorised, new List<ClaimToThing>(),
 | 
			
		||||
                    requestIdKey, isCached, new CacheOptions(reRoute.FileCacheOptions.TtlSeconds));
 | 
			
		||||
                return requestIdKey;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private string BuildUpstreamTemplate(FileReRoute reRoute)
 | 
			
		||||
        private string BuildReRouteKey(FileReRoute fileReRoute)
 | 
			
		||||
        {
 | 
			
		||||
            var upstreamTemplate = reRoute.UpstreamTemplate;
 | 
			
		||||
            //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
 | 
			
		||||
            var loadBalancerKey = $"{fileReRoute.UpstreamPathTemplate}{fileReRoute.UpstreamHttpMethod}";
 | 
			
		||||
            return loadBalancerKey;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private AuthenticationOptions BuildAuthenticationOptions(FileReRoute fileReRoute)
 | 
			
		||||
        {
 | 
			
		||||
            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('/');
 | 
			
		||||
 | 
			
		||||
@@ -140,6 +291,11 @@ namespace Ocelot.Configuration.Creator
 | 
			
		||||
                upstreamTemplate = upstreamTemplate.Replace(placeholder, RegExMatchEverything);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (upstreamTemplate == "/")
 | 
			
		||||
            {
 | 
			
		||||
                return RegExForwardSlashOnly;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var route = reRoute.ReRouteIsCaseSensitive 
 | 
			
		||||
                ? $"{upstreamTemplate}{RegExMatchEndString}" 
 | 
			
		||||
                : $"{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}";
 | 
			
		||||
@@ -147,7 +303,7 @@ namespace Ocelot.Configuration.Creator
 | 
			
		||||
            return route;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private List<ClaimToThing> GetAddThingsToRequest(Dictionary<string,string> thingBeingAdded)
 | 
			
		||||
        private List<ClaimToThing> BuildAddThingsToRequest(Dictionary<string,string> thingBeingAdded)
 | 
			
		||||
        {
 | 
			
		||||
            var claimsToTHings = new List<ClaimToThing>();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,12 @@
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Ocelot.Configuration.File;
 | 
			
		||||
using Ocelot.Responses;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Configuration.Creator
 | 
			
		||||
{
 | 
			
		||||
    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 FileGlobalConfiguration()
 | 
			
		||||
        {
 | 
			
		||||
            ServiceDiscoveryProvider = new FileServiceDiscoveryProvider();
 | 
			
		||||
            RateLimitOptions = new FileRateLimitOptions();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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>();
 | 
			
		||||
            AuthenticationOptions = new FileAuthenticationOptions();
 | 
			
		||||
            FileCacheOptions = new FileCacheOptions();
 | 
			
		||||
            QoSOptions = new FileQoSOptions();
 | 
			
		||||
            RateLimitOptions = new FileRateLimitRule();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string DownstreamTemplate { get; set; }
 | 
			
		||||
        public string UpstreamTemplate { get; set; }
 | 
			
		||||
        public string DownstreamPathTemplate { get; set; }
 | 
			
		||||
        public string UpstreamPathTemplate { get; set; }
 | 
			
		||||
        public string UpstreamHttpMethod { get; set; }
 | 
			
		||||
        public FileAuthenticationOptions AuthenticationOptions { get; set; }
 | 
			
		||||
        public Dictionary<string, string> AddHeadersToRequest { get; set; }
 | 
			
		||||
@@ -25,5 +27,12 @@ namespace Ocelot.Configuration.File
 | 
			
		||||
        public string RequestIdKey { get; set; }
 | 
			
		||||
        public FileCacheOptions FileCacheOptions { 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
 | 
			
		||||
    {
 | 
			
		||||
        List<ReRoute> ReRoutes { get; }
 | 
			
		||||
        string AdministrationPath {get;}
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -4,11 +4,13 @@ namespace Ocelot.Configuration
 | 
			
		||||
{
 | 
			
		||||
    public class OcelotConfiguration : IOcelotConfiguration
 | 
			
		||||
    {
 | 
			
		||||
        public OcelotConfiguration(List<ReRoute> reRoutes)
 | 
			
		||||
        public OcelotConfiguration(List<ReRoute> reRoutes, string administrationPath)
 | 
			
		||||
        {
 | 
			
		||||
            ReRoutes = reRoutes;
 | 
			
		||||
            AdministrationPath = administrationPath;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Configuration.Provider
 | 
			
		||||
@@ -10,13 +9,10 @@ namespace Ocelot.Configuration.Provider
 | 
			
		||||
    public class OcelotConfigurationProvider : IOcelotConfigurationProvider
 | 
			
		||||
    {
 | 
			
		||||
        private readonly IOcelotConfigurationRepository _repo;
 | 
			
		||||
        private readonly IOcelotConfigurationCreator _creator;
 | 
			
		||||
 | 
			
		||||
        public OcelotConfigurationProvider(IOcelotConfigurationRepository repo, 
 | 
			
		||||
            IOcelotConfigurationCreator creator)
 | 
			
		||||
        public OcelotConfigurationProvider(IOcelotConfigurationRepository repo)
 | 
			
		||||
        {
 | 
			
		||||
            _repo = repo;
 | 
			
		||||
            _creator = creator;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Response<IOcelotConfiguration> Get()
 | 
			
		||||
@@ -28,20 +24,6 @@ namespace Ocelot.Configuration.Provider
 | 
			
		||||
                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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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.Net.Http;
 | 
			
		||||
using Ocelot.Values;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Configuration
 | 
			
		||||
{
 | 
			
		||||
    public class ReRoute
 | 
			
		||||
    {
 | 
			
		||||
        public ReRoute(string downstreamTemplate, string upstreamTemplate, string upstreamHttpMethod, 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)
 | 
			
		||||
        public ReRoute(PathTemplate downstreamPathTemplate, 
 | 
			
		||||
            PathTemplate upstreamTemplate, 
 | 
			
		||||
            HttpMethod upstreamHttpMethod, 
 | 
			
		||||
            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;
 | 
			
		||||
            UpstreamTemplate = upstreamTemplate;
 | 
			
		||||
            ReRouteKey = reRouteKey;
 | 
			
		||||
            ServiceProviderConfiguraion = serviceProviderConfiguraion;
 | 
			
		||||
            LoadBalancer = loadBalancer;
 | 
			
		||||
            DownstreamHost = downstreamHost;
 | 
			
		||||
            DownstreamPort = downstreamPort;
 | 
			
		||||
            DownstreamPathTemplate = downstreamPathTemplate;
 | 
			
		||||
            UpstreamPathTemplate = upstreamTemplate;
 | 
			
		||||
            UpstreamHttpMethod = upstreamHttpMethod;
 | 
			
		||||
            UpstreamTemplatePattern = upstreamTemplatePattern;
 | 
			
		||||
            IsAuthenticated = isAuthenticated;
 | 
			
		||||
@@ -26,12 +53,18 @@ namespace Ocelot.Configuration
 | 
			
		||||
                ?? new List<ClaimToThing>();
 | 
			
		||||
            ClaimsToHeaders = configurationHeaderExtractorProperties 
 | 
			
		||||
                ?? new List<ClaimToThing>();
 | 
			
		||||
                DownstreamScheme = downstreamScheme;
 | 
			
		||||
            IsQos = isQos;
 | 
			
		||||
            QosOptions = qos;
 | 
			
		||||
            EnableEndpointRateLimiting = enableRateLimit;
 | 
			
		||||
            RateLimitOptions = ratelimitOptions;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string DownstreamTemplate { get; private set; }
 | 
			
		||||
        public string UpstreamTemplate { get; private set; }
 | 
			
		||||
        public string ReRouteKey {get;private set;}
 | 
			
		||||
        public PathTemplate DownstreamPathTemplate { get; private set; }
 | 
			
		||||
        public PathTemplate UpstreamPathTemplate { 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 IsAuthorised { get; private set; }
 | 
			
		||||
        public AuthenticationOptions AuthenticationOptions { get; private set; }
 | 
			
		||||
@@ -42,5 +75,14 @@ namespace Ocelot.Configuration
 | 
			
		||||
        public string RequestIdKey { get; private set; }
 | 
			
		||||
        public bool IsCached { 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);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            result = CheckForReRoutesContainingDownstreamSchemeInDownstreamPathTemplate(configuration);
 | 
			
		||||
 | 
			
		||||
            if (result.IsError)
 | 
			
		||||
            {
 | 
			
		||||
                return new OkResponse<ConfigurationValidationResult>(result);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return new OkResponse<ConfigurationValidationResult>(result);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -47,7 +54,7 @@ namespace Ocelot.Configuration.Validator
 | 
			
		||||
                    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);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -63,21 +70,42 @@ namespace Ocelot.Configuration.Validator
 | 
			
		||||
            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)
 | 
			
		||||
        {
 | 
			
		||||
            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)
 | 
			
		||||
            {
 | 
			
		||||
                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());
 | 
			
		||||
 | 
			
		||||
            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>()
 | 
			
		||||
                .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.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 Microsoft.Extensions.Logging;
 | 
			
		||||
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;
 | 
			
		||||
using FileConfigurationProvider = Ocelot.Configuration.Provider.FileConfigurationProvider;
 | 
			
		||||
using Ocelot.RateLimit;
 | 
			
		||||
 | 
			
		||||
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 AddOcelotFileConfiguration(this IServiceCollection services, IConfigurationRoot configurationRoot)
 | 
			
		||||
 | 
			
		||||
        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>();
 | 
			
		||||
 | 
			
		||||
            return services;
 | 
			
		||||
        }
 | 
			
		||||
            var identityServerConfiguration = IdentityServerConfigurationCreator.GetIdentityServerConfiguration();
 | 
			
		||||
            
 | 
			
		||||
        public static IServiceCollection AddOcelot(this IServiceCollection services)
 | 
			
		||||
        {
 | 
			
		||||
            services.AddMvcCore().AddJsonFormatters();
 | 
			
		||||
            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>();
 | 
			
		||||
@@ -69,7 +126,7 @@ namespace Ocelot.DependencyInjection
 | 
			
		||||
            services.AddSingleton<IClaimsParser, ClaimsParser>();
 | 
			
		||||
            services.AddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>();
 | 
			
		||||
            services.AddSingleton<IUrlPathPlaceholderNameAndValueFinder, UrlPathPlaceholderNameAndValueFinder>();
 | 
			
		||||
            services.AddSingleton<IDownstreamUrlPathPlaceholderReplacer, DownstreamUrlPathPlaceholderReplacer>();
 | 
			
		||||
            services.AddSingleton<IDownstreamPathPlaceholderReplacer, DownstreamTemplatePathPlaceholderReplacer>();
 | 
			
		||||
            services.AddSingleton<IDownstreamRouteFinder, DownstreamRouteFinder.Finder.DownstreamRouteFinder>();
 | 
			
		||||
            services.AddSingleton<IHttpRequester, HttpClientHttpRequester>();
 | 
			
		||||
            services.AddSingleton<IHttpResponder, HttpContextResponder>();
 | 
			
		||||
@@ -77,12 +134,13 @@ namespace Ocelot.DependencyInjection
 | 
			
		||||
            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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -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 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)
 | 
			
		||||
            {
 | 
			
		||||
                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);
 | 
			
		||||
 | 
			
		||||
                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));
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
using Microsoft.Extensions.Logging;
 | 
			
		||||
using Ocelot.DownstreamRouteFinder.Finder;
 | 
			
		||||
using Ocelot.Infrastructure.RequestData;
 | 
			
		||||
using Ocelot.Logging;
 | 
			
		||||
@@ -44,7 +43,7 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
 | 
			
		||||
                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);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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 Microsoft.AspNetCore.Http;
 | 
			
		||||
using Microsoft.Extensions.Logging;
 | 
			
		||||
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
 | 
			
		||||
using Ocelot.Infrastructure.RequestData;
 | 
			
		||||
using Ocelot.Logging;
 | 
			
		||||
@@ -11,17 +10,20 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
 | 
			
		||||
    public class DownstreamUrlCreatorMiddleware : OcelotMiddleware
 | 
			
		||||
    {
 | 
			
		||||
        private readonly RequestDelegate _next;
 | 
			
		||||
        private readonly IDownstreamUrlPathPlaceholderReplacer _urlReplacer;
 | 
			
		||||
        private readonly IDownstreamPathPlaceholderReplacer _replacer;
 | 
			
		||||
        private readonly IOcelotLogger _logger;
 | 
			
		||||
        private readonly IUrlBuilder _urlBuilder;
 | 
			
		||||
 | 
			
		||||
        public DownstreamUrlCreatorMiddleware(RequestDelegate next,
 | 
			
		||||
            IOcelotLoggerFactory loggerFactory,
 | 
			
		||||
            IDownstreamUrlPathPlaceholderReplacer urlReplacer,
 | 
			
		||||
            IRequestScopedDataRepository requestScopedDataRepository)
 | 
			
		||||
            IDownstreamPathPlaceholderReplacer replacer,
 | 
			
		||||
            IRequestScopedDataRepository requestScopedDataRepository, 
 | 
			
		||||
            IUrlBuilder urlBuilder)
 | 
			
		||||
            :base(requestScopedDataRepository)
 | 
			
		||||
        {
 | 
			
		||||
            _next = next;
 | 
			
		||||
            _urlReplacer = urlReplacer;
 | 
			
		||||
            _replacer = replacer;
 | 
			
		||||
            _urlBuilder = urlBuilder;
 | 
			
		||||
            _logger = loggerFactory.CreateLogger<DownstreamUrlCreatorMiddleware>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -29,19 +31,34 @@ namespace Ocelot.DownstreamUrlCreator.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;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            _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");
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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.Text;
 | 
			
		||||
using Ocelot.DownstreamRouteFinder;
 | 
			
		||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
 | 
			
		||||
using Ocelot.Responses;
 | 
			
		||||
using Ocelot.Values;
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
            {
 | 
			
		||||
                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 Ocelot.DownstreamRouteFinder.UrlMatcher;
 | 
			
		||||
using Ocelot.Responses;
 | 
			
		||||
using Ocelot.Values;
 | 
			
		||||
 | 
			
		||||
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.IO;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
using Microsoft.Extensions.Logging;
 | 
			
		||||
using Ocelot.Infrastructure.RequestData;
 | 
			
		||||
using Ocelot.Logging;
 | 
			
		||||
 | 
			
		||||
@@ -41,13 +39,13 @@ namespace Ocelot.Errors.Middleware
 | 
			
		||||
 | 
			
		||||
                var message = CreateMessage(context, e);
 | 
			
		||||
                _logger.LogError(message, e);
 | 
			
		||||
                await SetInternalServerErrorOnResponse(context);
 | 
			
		||||
                SetInternalServerErrorOnResponse(context);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            _logger.LogDebug("ocelot pipeline finished");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private async Task SetInternalServerErrorOnResponse(HttpContext context)
 | 
			
		||||
        private void SetInternalServerErrorOnResponse(HttpContext context)
 | 
			
		||||
        {
 | 
			
		||||
            context.Response.OnStarting(x =>
 | 
			
		||||
            {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
    {
 | 
			
		||||
        UnauthenticatedError, 
 | 
			
		||||
        UnknownError,
 | 
			
		||||
        DownstreamTemplateAlreadyUsedError,
 | 
			
		||||
        DownstreampathTemplateAlreadyUsedError,
 | 
			
		||||
        UnableToFindDownstreamRouteError,
 | 
			
		||||
        CannotAddDataError,
 | 
			
		||||
        CannotFindDataError,
 | 
			
		||||
@@ -17,6 +17,16 @@
 | 
			
		||||
        InstructionNotForClaimsError,
 | 
			
		||||
        UnauthorizedError,
 | 
			
		||||
        ClaimValueNotAuthorisedError,
 | 
			
		||||
        UserDoesNotHaveClaimError
 | 
			
		||||
        UserDoesNotHaveClaimError,
 | 
			
		||||
        DownstreamPathTemplateContainsSchemeError,
 | 
			
		||||
        DownstreamPathNullOrEmptyError,
 | 
			
		||||
        DownstreamSchemeNullOrEmptyError,
 | 
			
		||||
        DownstreamHostNullOrEmptyError,
 | 
			
		||||
        ServicesAreNullError,
 | 
			
		||||
        ServicesAreEmptyError,
 | 
			
		||||
        UnableToFindServiceDiscoveryProviderError,
 | 
			
		||||
        UnableToFindLoadBalancerError,
 | 
			
		||||
        RequestTimedOutError,
 | 
			
		||||
        UnableToFindQoSProviderError
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
using Microsoft.Extensions.Logging;
 | 
			
		||||
using Ocelot.Infrastructure.RequestData;
 | 
			
		||||
using Ocelot.Logging;
 | 
			
		||||
using Ocelot.Middleware;
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,6 @@ using Ocelot.Responses;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Headers
 | 
			
		||||
{
 | 
			
		||||
    using System;
 | 
			
		||||
 | 
			
		||||
    public class RemoveOutputHeaders : IRemoveOutputHeaders
 | 
			
		||||
    {
 | 
			
		||||
        /// <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