mirror of
				https://github.com/nsnail/Ocelot.git
				synced 2025-11-04 20:30:50 +08:00 
			
		
		
		
	Merge branch 'develop' into feature/Optimization-HttpClient-instance
This commit is contained in:
		
							
								
								
									
										519
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										519
									
								
								README.md
									
									
									
									
									
								
							@@ -26,531 +26,34 @@ it reaches a request builder middleware where it creates a HttpRequestMessage ob
 | 
				
			|||||||
used to make a request to a downstream service. The middleware that makes the request 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 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 
 | 
					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 
 | 
					and retrieved 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 maps the HttpResponseMessage onto the HttpResponse object and that is returned to the client.
 | 
				
			||||||
That is basically it with a bunch of other features.
 | 
					That is basically it with a bunch of other features.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 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. 
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## Building Ocelot
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
You should be able to just build Ocelot using the ./build.ps1 or ./build.sh scripts if you are on 
 | 
					 | 
				
			||||||
windows or mac. Alternatively you can build the project in VS2015 with the latest .NET Core SDK.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The tests should all just run and work apart from the integration tests which need the following 
 | 
					 | 
				
			||||||
environmental variables setting. This is a manual step at the moment.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    OCELOT_USERNAME=admin
 | 
					 | 
				
			||||||
    OCELOT_HASH=kE/mxd1hO9h9Sl2VhGhwJUd9xZEv4NP6qXoN39nIqM4=
 | 
					 | 
				
			||||||
    OCELOT_SALT=zzWITpnDximUNKYLiUam/w==
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
On windows you can use..
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	SETX OCELOT_USERNAME admin
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
On mac..
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	export OCELOT_USERNAME=admin
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
I need to work out a nicer way of doing this in the future.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## How to install
 | 
					## How to install
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Ocelot is designed to work with ASP.NET core only and is currently 
 | 
					Ocelot is designed to work with ASP.NET core only and is currently 
 | 
				
			||||||
built to netcoreapp1.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.
 | 
					built to netcoreapp1.1 [this](https://docs.microsoft.com/en-us/dotnet/articles/standard/library) documentation may prove helpful when working out if Ocelot would be suitable for you.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Install Ocelot and it's dependecies using nuget. At the moment 
 | 
					Install Ocelot and it's dependencies using NuGet. 
 | 
				
			||||||
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`
 | 
					`Install-Package Ocelot`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
All versions can be found [here](https://www.nuget.org/packages/Ocelot/)
 | 
					All versions can be found [here](https://www.nuget.org/packages/Ocelot/)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Configuration
 | 
					## Documentation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
An example configuration can be found [here](https://github.com/TomPallister/Ocelot/blob/develop/test/Ocelot.ManualTest/configuration.json) 
 | 
					Please click [here](https://github.com/TomPallister/Ocelot/wiki) for the Ocleot documentation. This includes lots of information and will be helpful if you want to understand the features Ocelot currently offers.
 | 
				
			||||||
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. 
 | 
					## Coming up
 | 
				
			||||||
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
 | 
					You can see what we are working on [here](https://github.com/TomPallister/Ocelot/projects/1)
 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    "ReRoutes": [],
 | 
					 | 
				
			||||||
    "GlobalConfiguration": {}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
More information on how to use these options is below..
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Startup
 | 
					## Contributing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
An example startup using a json file for configuration can be seen below. 
 | 
					Pull requests, issues and commentary welcome! No special process just create a request and get in 
 | 
				
			||||||
Currently this is the only way to get configuration into Ocelot.
 | 
					touch either via gitter or create an issue. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```csharp
 | 
					 | 
				
			||||||
public class Startup
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    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) =>
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            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.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```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! 
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## 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.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```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.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```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
 | 
					## Things that are currently annoying me
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -562,9 +65,5 @@ 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)
 | 
					[ 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)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1 +1 @@
 | 
				
			|||||||
{"ReRoutes":[],"GlobalConfiguration":{"RequestIdKey":null,"ServiceDiscoveryProvider":{"Provider":null,"Host":null,"Port":0},"AdministrationPath":"/administration"}}
 | 
					{"ReRoutes":[{"DownstreamPathTemplate":"/","UpstreamPathTemplate":"/","UpstreamHttpMethod":"Get","AuthenticationOptions":{"Provider":null,"ProviderRootUrl":null,"ScopeName":null,"RequireHttps":false,"AdditionalScopes":[],"ScopeSecret":null},"AddHeadersToRequest":{},"AddClaimsToRequest":{},"RouteClaimsRequirement":{},"AddQueriesToRequest":{},"RequestIdKey":null,"FileCacheOptions":{"TtlSeconds":0},"ReRouteIsCaseSensitive":false,"ServiceName":null,"DownstreamScheme":"http","DownstreamHost":"localhost","DownstreamPort":51879,"QoSOptions":{"ExceptionsAllowedBeforeBreaking":0,"DurationOfBreak":0,"TimeoutValue":0},"LoadBalancer":null,"RateLimitOptions":{"ClientWhitelist":[],"EnableRateLimiting":false,"Period":null,"PeriodTimespan":0.0,"Limit":0}}],"GlobalConfiguration":{"RequestIdKey":null,"ServiceDiscoveryProvider":{"Provider":null,"Host":null,"Port":0},"AdministrationPath":null,"RateLimitOptions":{"ClientIdHeader":"ClientId","QuotaExceededMessage":null,"RateLimitCounterPrefix":"ocelot","DisableRateLimitHeaders":false,"HttpStatusCode":429}}}
 | 
				
			||||||
							
								
								
									
										71
									
								
								src/Ocelot/Configuration/Builder/RateLimitOptionsBuilder.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								src/Ocelot/Configuration/Builder/RateLimitOptionsBuilder.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
				
			|||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Builder
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class RateLimitOptionsBuilder
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private bool _enableRateLimiting;
 | 
				
			||||||
 | 
					        private string _clientIdHeader;
 | 
				
			||||||
 | 
					        private List<string> _clientWhitelist;
 | 
				
			||||||
 | 
					        private bool _disableRateLimitHeaders;
 | 
				
			||||||
 | 
					        private string _quotaExceededMessage;
 | 
				
			||||||
 | 
					        private string _rateLimitCounterPrefix;
 | 
				
			||||||
 | 
					        private RateLimitRule _rateLimitRule;
 | 
				
			||||||
 | 
					        private int _httpStatusCode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public RateLimitOptionsBuilder WithEnableRateLimiting(bool enableRateLimiting)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _enableRateLimiting = enableRateLimiting;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public RateLimitOptionsBuilder WithClientIdHeader(string clientIdheader)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _clientIdHeader = clientIdheader;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public RateLimitOptionsBuilder WithClientWhiteList(List<string> clientWhitelist)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _clientWhitelist = clientWhitelist;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public RateLimitOptionsBuilder WithDisableRateLimitHeaders(bool disableRateLimitHeaders)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _disableRateLimitHeaders = disableRateLimitHeaders;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public RateLimitOptionsBuilder WithQuotaExceededMessage(string quotaExceededMessage)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _quotaExceededMessage = quotaExceededMessage;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public RateLimitOptionsBuilder WithRateLimitCounterPrefix(string rateLimitCounterPrefix)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _rateLimitCounterPrefix = rateLimitCounterPrefix;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public RateLimitOptionsBuilder WithRateLimitRule(RateLimitRule rateLimitRule)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _rateLimitRule = rateLimitRule;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public RateLimitOptionsBuilder WithHttpStatusCode(int httpStatusCode)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _httpStatusCode = httpStatusCode;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public RateLimitOptions Build()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return new RateLimitOptions(_enableRateLimiting, _clientIdHeader, _clientWhitelist, 
 | 
				
			||||||
 | 
					                _disableRateLimitHeaders, _quotaExceededMessage, _rateLimitCounterPrefix, 
 | 
				
			||||||
 | 
					                _rateLimitRule, _httpStatusCode);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -25,7 +25,7 @@ namespace Ocelot.Configuration.Builder
 | 
				
			|||||||
        private string _downstreamHost;
 | 
					        private string _downstreamHost;
 | 
				
			||||||
        private int _downstreamPort;
 | 
					        private int _downstreamPort;
 | 
				
			||||||
        private string _loadBalancer;
 | 
					        private string _loadBalancer;
 | 
				
			||||||
        private ServiceProviderConfiguraion _serviceProviderConfiguraion;
 | 
					        private ServiceProviderConfiguration _serviceProviderConfiguraion;
 | 
				
			||||||
        private bool _useQos;
 | 
					        private bool _useQos;
 | 
				
			||||||
        private QoSOptions _qosOptions;
 | 
					        private QoSOptions _qosOptions;
 | 
				
			||||||
        public bool _enableRateLimiting;
 | 
					        public bool _enableRateLimiting;
 | 
				
			||||||
@@ -150,7 +150,7 @@ namespace Ocelot.Configuration.Builder
 | 
				
			|||||||
            return this;
 | 
					            return this;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ReRouteBuilder WithServiceProviderConfiguraion(ServiceProviderConfiguraion serviceProviderConfiguraion)
 | 
					        public ReRouteBuilder WithServiceProviderConfiguraion(ServiceProviderConfiguration serviceProviderConfiguraion)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _serviceProviderConfiguraion = serviceProviderConfiguraion;
 | 
					            _serviceProviderConfiguraion = serviceProviderConfiguraion;
 | 
				
			||||||
            return this;
 | 
					            return this;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										46
									
								
								src/Ocelot/Configuration/Builder/ReRouteOptionsBuilder.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/Ocelot/Configuration/Builder/ReRouteOptionsBuilder.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					namespace Ocelot.Configuration.Builder
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class ReRouteOptionsBuilder
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private bool _isAuthenticated;
 | 
				
			||||||
 | 
					        private bool _isAuthorised;
 | 
				
			||||||
 | 
					        private bool _isCached;
 | 
				
			||||||
 | 
					        private bool _isQoS;
 | 
				
			||||||
 | 
					        private bool _enableRateLimiting;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ReRouteOptionsBuilder WithIsCached(bool isCached)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _isCached = isCached;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ReRouteOptionsBuilder WithIsAuthenticated(bool isAuthenticated)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _isAuthenticated = isAuthenticated;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ReRouteOptionsBuilder WithIsAuthorised(bool isAuthorised)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _isAuthorised = isAuthorised;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ReRouteOptionsBuilder WithIsQos(bool isQoS)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _isQoS = isQoS;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ReRouteOptionsBuilder WithRateLimiting(bool enableRateLimiting)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _enableRateLimiting = enableRateLimiting;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ReRouteOptions Build()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return new ReRouteOptions(_isAuthenticated, _isAuthorised, _isCached, _isQoS, _enableRateLimiting);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
namespace Ocelot.Configuration.Builder
 | 
					namespace Ocelot.Configuration.Builder
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class ServiceProviderConfiguraionBuilder
 | 
					    public class ServiceProviderConfigurationBuilder
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private string _serviceName;
 | 
					        private string _serviceName;
 | 
				
			||||||
        private string _downstreamHost;
 | 
					        private string _downstreamHost;
 | 
				
			||||||
@@ -10,52 +10,52 @@ namespace Ocelot.Configuration.Builder
 | 
				
			|||||||
        private string _serviceDiscoveryProviderHost;
 | 
					        private string _serviceDiscoveryProviderHost;
 | 
				
			||||||
        private int _serviceDiscoveryProviderPort;
 | 
					        private int _serviceDiscoveryProviderPort;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ServiceProviderConfiguraionBuilder WithServiceName(string serviceName)
 | 
					        public ServiceProviderConfigurationBuilder WithServiceName(string serviceName)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _serviceName = serviceName;
 | 
					            _serviceName = serviceName;
 | 
				
			||||||
            return this;
 | 
					            return this;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ServiceProviderConfiguraionBuilder WithDownstreamHost(string downstreamHost)
 | 
					        public ServiceProviderConfigurationBuilder WithDownstreamHost(string downstreamHost)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _downstreamHost = downstreamHost;
 | 
					            _downstreamHost = downstreamHost;
 | 
				
			||||||
            return this;
 | 
					            return this;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ServiceProviderConfiguraionBuilder WithDownstreamPort(int downstreamPort)
 | 
					        public ServiceProviderConfigurationBuilder WithDownstreamPort(int downstreamPort)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _downstreamPort = downstreamPort;
 | 
					            _downstreamPort = downstreamPort;
 | 
				
			||||||
            return this;
 | 
					            return this;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ServiceProviderConfiguraionBuilder WithUseServiceDiscovery(bool userServiceDiscovery)
 | 
					        public ServiceProviderConfigurationBuilder WithUseServiceDiscovery(bool userServiceDiscovery)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _userServiceDiscovery = userServiceDiscovery;
 | 
					            _userServiceDiscovery = userServiceDiscovery;
 | 
				
			||||||
            return this;
 | 
					            return this;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ServiceProviderConfiguraionBuilder WithServiceDiscoveryProvider(string serviceDiscoveryProvider)
 | 
					        public ServiceProviderConfigurationBuilder WithServiceDiscoveryProvider(string serviceDiscoveryProvider)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _serviceDiscoveryProvider = serviceDiscoveryProvider;
 | 
					            _serviceDiscoveryProvider = serviceDiscoveryProvider;
 | 
				
			||||||
            return this;
 | 
					            return this;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ServiceProviderConfiguraionBuilder WithServiceDiscoveryProviderHost(string serviceDiscoveryProviderHost)
 | 
					        public ServiceProviderConfigurationBuilder WithServiceDiscoveryProviderHost(string serviceDiscoveryProviderHost)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _serviceDiscoveryProviderHost = serviceDiscoveryProviderHost;
 | 
					            _serviceDiscoveryProviderHost = serviceDiscoveryProviderHost;
 | 
				
			||||||
            return this;
 | 
					            return this;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ServiceProviderConfiguraionBuilder WithServiceDiscoveryProviderPort(int serviceDiscoveryProviderPort)
 | 
					        public ServiceProviderConfigurationBuilder WithServiceDiscoveryProviderPort(int serviceDiscoveryProviderPort)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _serviceDiscoveryProviderPort = serviceDiscoveryProviderPort;
 | 
					            _serviceDiscoveryProviderPort = serviceDiscoveryProviderPort;
 | 
				
			||||||
            return this;
 | 
					            return this;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        public ServiceProviderConfiguraion Build()
 | 
					        public ServiceProviderConfiguration Build()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return new ServiceProviderConfiguraion(_serviceName, _downstreamHost, _downstreamPort, _userServiceDiscovery,
 | 
					            return new ServiceProviderConfiguration(_serviceName, _downstreamHost, _downstreamPort, _userServiceDiscovery,
 | 
				
			||||||
            _serviceDiscoveryProvider, _serviceDiscoveryProviderHost,_serviceDiscoveryProviderPort);
 | 
					            _serviceDiscoveryProvider, _serviceDiscoveryProviderHost,_serviceDiscoveryProviderPort);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					using Ocelot.Configuration.Builder;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Creator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class AuthenticationOptionsCreator : IAuthenticationOptionsCreator
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public AuthenticationOptions Create(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();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										41
									
								
								src/Ocelot/Configuration/Creator/ClaimsToThingCreator.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/Ocelot/Configuration/Creator/ClaimsToThingCreator.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Parser;
 | 
				
			||||||
 | 
					using Ocelot.Logging;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Creator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class ClaimsToThingCreator : IClaimsToThingCreator
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private readonly IClaimToThingConfigurationParser _claimToThingConfigParser;
 | 
				
			||||||
 | 
					        private readonly IOcelotLogger _logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ClaimsToThingCreator(IClaimToThingConfigurationParser claimToThingConfigurationParser,
 | 
				
			||||||
 | 
					            IOcelotLoggerFactory loggerFactory)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _logger = loggerFactory.CreateLogger<ClaimsToThingCreator>();
 | 
				
			||||||
 | 
					            _claimToThingConfigParser = claimToThingConfigurationParser;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public List<ClaimToThing> Create(Dictionary<string,string> inputToBeParsed)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var claimsToThings = new List<ClaimToThing>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            foreach (var input in inputToBeParsed)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var claimToThing = _claimToThingConfigParser.Extract(input.Key, input.Value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (claimToThing.IsError)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    _logger.LogDebug("ClaimsToThingCreator.BuildAddThingsToRequest",
 | 
				
			||||||
 | 
					                        $"Unable to extract configuration for key: {input.Key} and value: {input.Value} your configuration file is incorrect");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    claimsToThings.Add(claimToThing.Data);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return claimsToThings;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -22,36 +22,53 @@ namespace Ocelot.Configuration.Creator
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly IOptions<FileConfiguration> _options;
 | 
					        private readonly IOptions<FileConfiguration> _options;
 | 
				
			||||||
        private readonly IConfigurationValidator _configurationValidator;
 | 
					        private readonly IConfigurationValidator _configurationValidator;
 | 
				
			||||||
        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 ILogger<FileOcelotConfigurationCreator> _logger;
 | 
				
			||||||
        private readonly ILoadBalancerFactory _loadBalanceFactory;
 | 
					        private readonly ILoadBalancerFactory _loadBalanceFactory;
 | 
				
			||||||
        private readonly ILoadBalancerHouse _loadBalancerHouse;
 | 
					        private readonly ILoadBalancerHouse _loadBalancerHouse;
 | 
				
			||||||
        private readonly IQoSProviderFactory _qoSProviderFactory;
 | 
					        private readonly IQoSProviderFactory _qoSProviderFactory;
 | 
				
			||||||
        private readonly IQosProviderHouse _qosProviderHouse;
 | 
					        private readonly IQosProviderHouse _qosProviderHouse;
 | 
				
			||||||
 | 
					        private readonly IClaimsToThingCreator _claimsToThingCreator;
 | 
				
			||||||
 | 
					        private readonly IAuthenticationOptionsCreator _authOptionsCreator;
 | 
				
			||||||
 | 
					        private IUpstreamTemplatePatternCreator _upstreamTemplatePatternCreator;
 | 
				
			||||||
 | 
					        private IRequestIdKeyCreator _requestIdKeyCreator;
 | 
				
			||||||
 | 
					        private IServiceProviderConfigurationCreator _serviceProviderConfigCreator;
 | 
				
			||||||
 | 
					        private IQoSOptionsCreator _qosOptionsCreator;
 | 
				
			||||||
 | 
					        private IReRouteOptionsCreator _fileReRouteOptionsCreator;
 | 
				
			||||||
 | 
					        private IRateLimitOptionsCreator _rateLimitOptionsCreator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public FileOcelotConfigurationCreator(
 | 
					        public FileOcelotConfigurationCreator(
 | 
				
			||||||
            IOptions<FileConfiguration> options, 
 | 
					            IOptions<FileConfiguration> options, 
 | 
				
			||||||
            IConfigurationValidator configurationValidator, 
 | 
					            IConfigurationValidator configurationValidator, 
 | 
				
			||||||
            IClaimToThingConfigurationParser claimToThingConfigurationParser, 
 | 
					 | 
				
			||||||
            ILogger<FileOcelotConfigurationCreator> logger,
 | 
					            ILogger<FileOcelotConfigurationCreator> logger,
 | 
				
			||||||
            ILoadBalancerFactory loadBalancerFactory,
 | 
					            ILoadBalancerFactory loadBalancerFactory,
 | 
				
			||||||
            ILoadBalancerHouse loadBalancerHouse, 
 | 
					            ILoadBalancerHouse loadBalancerHouse, 
 | 
				
			||||||
            IQoSProviderFactory qoSProviderFactory, 
 | 
					            IQoSProviderFactory qoSProviderFactory, 
 | 
				
			||||||
            IQosProviderHouse qosProviderHouse)
 | 
					            IQosProviderHouse qosProviderHouse,
 | 
				
			||||||
 | 
					            IClaimsToThingCreator claimsToThingCreator,
 | 
				
			||||||
 | 
					            IAuthenticationOptionsCreator authOptionsCreator,
 | 
				
			||||||
 | 
					            IUpstreamTemplatePatternCreator upstreamTemplatePatternCreator,
 | 
				
			||||||
 | 
					            IRequestIdKeyCreator requestIdKeyCreator,
 | 
				
			||||||
 | 
					            IServiceProviderConfigurationCreator serviceProviderConfigCreator,
 | 
				
			||||||
 | 
					            IQoSOptionsCreator qosOptionsCreator,
 | 
				
			||||||
 | 
					            IReRouteOptionsCreator fileReRouteOptionsCreator,
 | 
				
			||||||
 | 
					            IRateLimitOptionsCreator rateLimitOptionsCreator
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            _rateLimitOptionsCreator = rateLimitOptionsCreator;
 | 
				
			||||||
 | 
					            _requestIdKeyCreator = requestIdKeyCreator;
 | 
				
			||||||
 | 
					            _upstreamTemplatePatternCreator = upstreamTemplatePatternCreator;
 | 
				
			||||||
 | 
					            _authOptionsCreator = authOptionsCreator;
 | 
				
			||||||
            _loadBalanceFactory = loadBalancerFactory;
 | 
					            _loadBalanceFactory = loadBalancerFactory;
 | 
				
			||||||
            _loadBalancerHouse = loadBalancerHouse;
 | 
					            _loadBalancerHouse = loadBalancerHouse;
 | 
				
			||||||
            _qoSProviderFactory = qoSProviderFactory;
 | 
					            _qoSProviderFactory = qoSProviderFactory;
 | 
				
			||||||
            _qosProviderHouse = qosProviderHouse;
 | 
					            _qosProviderHouse = qosProviderHouse;
 | 
				
			||||||
            _options = options;
 | 
					            _options = options;
 | 
				
			||||||
            _configurationValidator = configurationValidator;
 | 
					            _configurationValidator = configurationValidator;
 | 
				
			||||||
            _claimToThingConfigurationParser = claimToThingConfigurationParser;
 | 
					 | 
				
			||||||
            _logger = logger;
 | 
					            _logger = logger;
 | 
				
			||||||
 | 
					            _claimsToThingCreator = claimsToThingCreator;
 | 
				
			||||||
 | 
					            _serviceProviderConfigCreator = serviceProviderConfigCreator;
 | 
				
			||||||
 | 
					            _qosOptionsCreator = qosOptionsCreator;
 | 
				
			||||||
 | 
					            _fileReRouteOptionsCreator = fileReRouteOptionsCreator;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task<Response<IOcelotConfiguration>> Create()
 | 
					        public async Task<Response<IOcelotConfiguration>> Create()
 | 
				
			||||||
@@ -97,50 +114,42 @@ namespace Ocelot.Configuration.Creator
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private async Task<ReRoute> SetUpReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration)
 | 
					        private async Task<ReRoute> SetUpReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var isAuthenticated = IsAuthenticated(fileReRoute);
 | 
					            var fileReRouteOptions = _fileReRouteOptionsCreator.Create(fileReRoute);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var isAuthorised = IsAuthorised(fileReRoute);
 | 
					            var requestIdKey = _requestIdKeyCreator.Create(fileReRoute, globalConfiguration);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var isCached = IsCached(fileReRoute);
 | 
					            var reRouteKey = CreateReRouteKey(fileReRoute);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var requestIdKey = BuildRequestId(fileReRoute, globalConfiguration);
 | 
					            var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileReRoute);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var reRouteKey = BuildReRouteKey(fileReRoute);
 | 
					            var serviceProviderConfiguration = _serviceProviderConfigCreator.Create(fileReRoute, globalConfiguration);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var upstreamTemplatePattern = BuildUpstreamTemplatePattern(fileReRoute);
 | 
					            var authOptionsForRoute = _authOptionsCreator.Create(fileReRoute);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var isQos = IsQoS(fileReRoute);
 | 
					            var claimsToHeaders = _claimsToThingCreator.Create(fileReRoute.AddHeadersToRequest);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var serviceProviderConfiguration = BuildServiceProviderConfiguration(fileReRoute, globalConfiguration);
 | 
					            var claimsToClaims = _claimsToThingCreator.Create(fileReRoute.AddClaimsToRequest);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var authOptionsForRoute = BuildAuthenticationOptions(fileReRoute);
 | 
					            var claimsToQueries = _claimsToThingCreator.Create(fileReRoute.AddQueriesToRequest);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var claimsToHeaders = BuildAddThingsToRequest(fileReRoute.AddHeadersToRequest);
 | 
					            var qosOptions = _qosOptionsCreator.Create(fileReRoute);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var claimsToClaims = BuildAddThingsToRequest(fileReRoute.AddClaimsToRequest);
 | 
					            var rateLimitOption = _rateLimitOptionsCreator.Create(fileReRoute, globalConfiguration, fileReRouteOptions.EnableRateLimiting);
 | 
				
			||||||
 | 
					 | 
				
			||||||
            var claimsToQueries = BuildAddThingsToRequest(fileReRoute.AddQueriesToRequest);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var qosOptions = BuildQoSOptions(fileReRoute);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var enableRateLimiting = IsEnableRateLimiting(fileReRoute);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var rateLimitOption = BuildRateLimitOptions(fileReRoute, globalConfiguration, enableRateLimiting);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var reRoute = new ReRouteBuilder()
 | 
					            var reRoute = new ReRouteBuilder()
 | 
				
			||||||
                .WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate)
 | 
					                .WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate)
 | 
				
			||||||
                .WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate)
 | 
					                .WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate)
 | 
				
			||||||
                .WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod)
 | 
					                .WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod)
 | 
				
			||||||
                .WithUpstreamTemplatePattern(upstreamTemplatePattern)
 | 
					                .WithUpstreamTemplatePattern(upstreamTemplatePattern)
 | 
				
			||||||
                .WithIsAuthenticated(isAuthenticated)
 | 
					                .WithIsAuthenticated(fileReRouteOptions.IsAuthenticated)
 | 
				
			||||||
                .WithAuthenticationOptions(authOptionsForRoute)
 | 
					                .WithAuthenticationOptions(authOptionsForRoute)
 | 
				
			||||||
                .WithClaimsToHeaders(claimsToHeaders)
 | 
					                .WithClaimsToHeaders(claimsToHeaders)
 | 
				
			||||||
                .WithClaimsToClaims(claimsToClaims)
 | 
					                .WithClaimsToClaims(claimsToClaims)
 | 
				
			||||||
                .WithRouteClaimsRequirement(fileReRoute.RouteClaimsRequirement)
 | 
					                .WithRouteClaimsRequirement(fileReRoute.RouteClaimsRequirement)
 | 
				
			||||||
                .WithIsAuthorised(isAuthorised)
 | 
					                .WithIsAuthorised(fileReRouteOptions.IsAuthorised)
 | 
				
			||||||
                .WithClaimsToQueries(claimsToQueries)
 | 
					                .WithClaimsToQueries(claimsToQueries)
 | 
				
			||||||
                .WithRequestIdKey(requestIdKey)
 | 
					                .WithRequestIdKey(requestIdKey)
 | 
				
			||||||
                .WithIsCached(isCached)
 | 
					                .WithIsCached(fileReRouteOptions.IsCached)
 | 
				
			||||||
                .WithCacheOptions(new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds))
 | 
					                .WithCacheOptions(new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds))
 | 
				
			||||||
                .WithDownstreamScheme(fileReRoute.DownstreamScheme)
 | 
					                .WithDownstreamScheme(fileReRoute.DownstreamScheme)
 | 
				
			||||||
                .WithLoadBalancer(fileReRoute.LoadBalancer)
 | 
					                .WithLoadBalancer(fileReRoute.LoadBalancer)
 | 
				
			||||||
@@ -148,95 +157,24 @@ namespace Ocelot.Configuration.Creator
 | 
				
			|||||||
                .WithDownstreamPort(fileReRoute.DownstreamPort)
 | 
					                .WithDownstreamPort(fileReRoute.DownstreamPort)
 | 
				
			||||||
                .WithLoadBalancerKey(reRouteKey)
 | 
					                .WithLoadBalancerKey(reRouteKey)
 | 
				
			||||||
                .WithServiceProviderConfiguraion(serviceProviderConfiguration)
 | 
					                .WithServiceProviderConfiguraion(serviceProviderConfiguration)
 | 
				
			||||||
                .WithIsQos(isQos)
 | 
					                .WithIsQos(fileReRouteOptions.IsQos)
 | 
				
			||||||
                .WithQosOptions(qosOptions)
 | 
					                .WithQosOptions(qosOptions)
 | 
				
			||||||
                .WithEnableRateLimiting(enableRateLimiting)
 | 
					                .WithEnableRateLimiting(fileReRouteOptions.EnableRateLimiting)
 | 
				
			||||||
                .WithRateLimitOptions(rateLimitOption)
 | 
					                .WithRateLimitOptions(rateLimitOption)
 | 
				
			||||||
                .Build();
 | 
					                .Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await SetupLoadBalancer(reRoute);
 | 
					            await SetupLoadBalancer(reRoute);
 | 
				
			||||||
            SetupQosProvider(reRoute);
 | 
					            SetupQosProvider(reRoute);
 | 
				
			||||||
            return reRoute;
 | 
					            return reRoute;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static RateLimitOptions BuildRateLimitOptions(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration, bool enableRateLimiting)
 | 
					        private string CreateReRouteKey(FileReRoute fileReRoute)
 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            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 requestIdKey = globalRequestIdConfiguration
 | 
					 | 
				
			||||||
                ? globalConfiguration.RequestIdKey
 | 
					 | 
				
			||||||
                : fileReRoute.RequestIdKey;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                return requestIdKey;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private string BuildReRouteKey(FileReRoute fileReRoute)
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            //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
 | 
					            //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}";
 | 
					            var loadBalancerKey = $"{fileReRoute.UpstreamPathTemplate}{fileReRoute.UpstreamHttpMethod}";
 | 
				
			||||||
            return loadBalancerKey;
 | 
					            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)
 | 
					        private async Task SetupLoadBalancer(ReRoute reRoute)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var loadBalancer = await _loadBalanceFactory.Get(reRoute);
 | 
					            var loadBalancer = await _loadBalanceFactory.Get(reRoute);
 | 
				
			||||||
@@ -248,85 +186,5 @@ namespace Ocelot.Configuration.Creator
 | 
				
			|||||||
            var loadBalancer = _qoSProviderFactory.Get(reRoute);
 | 
					            var loadBalancer = _qoSProviderFactory.Get(reRoute);
 | 
				
			||||||
            _qosProviderHouse.Add(reRoute.ReRouteKey, loadBalancer);
 | 
					            _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('/');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var placeholders = new List<string>();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            for (var i = 0; i < upstreamTemplate.Length; i++)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                if (IsPlaceHolder(upstreamTemplate, i))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    var postitionOfPlaceHolderClosingBracket = upstreamTemplate.IndexOf('}', i);
 | 
					 | 
				
			||||||
                    var difference = postitionOfPlaceHolderClosingBracket - i + 1;
 | 
					 | 
				
			||||||
                    var variableName = upstreamTemplate.Substring(i, difference);
 | 
					 | 
				
			||||||
                    placeholders.Add(variableName);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            foreach (var placeholder in placeholders)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                upstreamTemplate = upstreamTemplate.Replace(placeholder, RegExMatchEverything);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (upstreamTemplate == "/")
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return RegExForwardSlashOnly;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var route = reRoute.ReRouteIsCaseSensitive 
 | 
					 | 
				
			||||||
                ? $"{upstreamTemplate}{RegExMatchEndString}" 
 | 
					 | 
				
			||||||
                : $"{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return route;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private List<ClaimToThing> BuildAddThingsToRequest(Dictionary<string,string> thingBeingAdded)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var claimsToTHings = new List<ClaimToThing>();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            foreach (var add in thingBeingAdded)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var claimToHeader = _claimToThingConfigurationParser.Extract(add.Key, add.Value);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (claimToHeader.IsError)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _logger.LogCritical(new EventId(1, "Application Failed to start"),
 | 
					 | 
				
			||||||
                        $"Unable to extract configuration for key: {add.Key} and value: {add.Value} your configuration file is incorrect");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    throw new Exception(claimToHeader.Errors[0].Message);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                claimsToTHings.Add(claimToHeader.Data);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return claimsToTHings;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private bool IsPlaceHolder(string upstreamTemplate, int i)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return upstreamTemplate[i] == '{';
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Creator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public interface IAuthenticationOptionsCreator
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        AuthenticationOptions Create(FileReRoute fileReRoute);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Creator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public interface IClaimsToThingCreator
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        List<ClaimToThing> Create(Dictionary<string,string> thingsBeingAdded);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										9
									
								
								src/Ocelot/Configuration/Creator/IQoSOptionsCreator.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/Ocelot/Configuration/Creator/IQoSOptionsCreator.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Creator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public interface IQoSOptionsCreator
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        QoSOptions Create(FileReRoute fileReRoute);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Creator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public interface IRateLimitOptionsCreator
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        RateLimitOptions Create(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration, bool enableRateLimiting);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Creator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public interface IReRouteOptionsCreator
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ReRouteOptions Create(FileReRoute fileReRoute);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										9
									
								
								src/Ocelot/Configuration/Creator/IRequestIdKeyCreator.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/Ocelot/Configuration/Creator/IRequestIdKeyCreator.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Creator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public interface IRequestIdKeyCreator
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        string Create(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Creator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public interface IServiceProviderConfigurationCreator
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ServiceProviderConfiguration Create(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Creator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public interface IUpstreamTemplatePatternCreator
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        string Create(FileReRoute reRoute);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										17
									
								
								src/Ocelot/Configuration/Creator/QoSOptionsCreator.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/Ocelot/Configuration/Creator/QoSOptionsCreator.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					using Ocelot.Configuration.Builder;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Creator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class QoSOptionsCreator : IQoSOptionsCreator
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public QoSOptions Create(FileReRoute fileReRoute)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return new QoSOptionsBuilder()
 | 
				
			||||||
 | 
					                .WithExceptionsAllowedBeforeBreaking(fileReRoute.QoSOptions.ExceptionsAllowedBeforeBreaking)
 | 
				
			||||||
 | 
					                .WithDurationOfBreak(fileReRoute.QoSOptions.DurationOfBreak)
 | 
				
			||||||
 | 
					                .WithTimeoutValue(fileReRoute.QoSOptions.TimeoutValue)
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										32
									
								
								src/Ocelot/Configuration/Creator/RateLimitOptionsCreator.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/Ocelot/Configuration/Creator/RateLimitOptionsCreator.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Builder;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Creator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class RateLimitOptionsCreator : IRateLimitOptionsCreator
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public RateLimitOptions Create(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration, bool enableRateLimiting)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            RateLimitOptions rateLimitOption = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (enableRateLimiting)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                rateLimitOption = new RateLimitOptionsBuilder()
 | 
				
			||||||
 | 
					                    .WithClientIdHeader(globalConfiguration.RateLimitOptions.ClientIdHeader)
 | 
				
			||||||
 | 
					                    .WithClientWhiteList(fileReRoute.RateLimitOptions.ClientWhitelist)
 | 
				
			||||||
 | 
					                    .WithDisableRateLimitHeaders(globalConfiguration.RateLimitOptions.DisableRateLimitHeaders)
 | 
				
			||||||
 | 
					                    .WithEnableRateLimiting(fileReRoute.RateLimitOptions.EnableRateLimiting)
 | 
				
			||||||
 | 
					                    .WithHttpStatusCode(globalConfiguration.RateLimitOptions.HttpStatusCode)
 | 
				
			||||||
 | 
					                    .WithQuotaExceededMessage(globalConfiguration.RateLimitOptions.QuotaExceededMessage)
 | 
				
			||||||
 | 
					                    .WithRateLimitCounterPrefix(globalConfiguration.RateLimitOptions.RateLimitCounterPrefix)
 | 
				
			||||||
 | 
					                    .WithRateLimitRule(new RateLimitRule(fileReRoute.RateLimitOptions.Period,
 | 
				
			||||||
 | 
					                        TimeSpan.FromSeconds(fileReRoute.RateLimitOptions.PeriodTimespan),
 | 
				
			||||||
 | 
					                        fileReRoute.RateLimitOptions.Limit))
 | 
				
			||||||
 | 
					                    .Build();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return rateLimitOption;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										52
									
								
								src/Ocelot/Configuration/Creator/ReRouteOptionsCreator.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/Ocelot/Configuration/Creator/ReRouteOptionsCreator.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
				
			|||||||
 | 
					using Ocelot.Configuration.Builder;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Creator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class ReRouteOptionsCreator : IReRouteOptionsCreator
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public ReRouteOptions Create(FileReRoute fileReRoute)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var isAuthenticated = IsAuthenticated(fileReRoute);
 | 
				
			||||||
 | 
					            var isAuthorised = IsAuthorised(fileReRoute);
 | 
				
			||||||
 | 
					            var isCached = IsCached(fileReRoute);
 | 
				
			||||||
 | 
					            var isQos = IsQoS(fileReRoute);
 | 
				
			||||||
 | 
					            var enableRateLimiting = IsEnableRateLimiting(fileReRoute);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var options = new ReRouteOptionsBuilder()
 | 
				
			||||||
 | 
					                .WithIsAuthenticated(isAuthenticated)
 | 
				
			||||||
 | 
					                .WithIsAuthorised(isAuthorised)
 | 
				
			||||||
 | 
					                .WithIsCached(isCached)
 | 
				
			||||||
 | 
					                .WithIsQos(isQos)
 | 
				
			||||||
 | 
					                .WithRateLimiting(enableRateLimiting)
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            return options;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private static bool IsEnableRateLimiting(FileReRoute fileReRoute)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return (fileReRoute.RateLimitOptions != null && fileReRoute.RateLimitOptions.EnableRateLimiting) ? true : false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										18
									
								
								src/Ocelot/Configuration/Creator/RequestIdKeyCreator.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/Ocelot/Configuration/Creator/RequestIdKeyCreator.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Creator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class RequestIdKeyCreator : IRequestIdKeyCreator
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public string Create(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var globalRequestIdConfiguration = !string.IsNullOrEmpty(globalConfiguration?.RequestIdKey);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					             var requestIdKey = globalRequestIdConfiguration
 | 
				
			||||||
 | 
					                ? globalConfiguration.RequestIdKey
 | 
				
			||||||
 | 
					                : fileReRoute.RequestIdKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return requestIdKey;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					using Ocelot.Configuration.Builder;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Creator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class ServiceProviderConfigurationCreator : IServiceProviderConfigurationCreator
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public ServiceProviderConfiguration Create(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var useServiceDiscovery = !string.IsNullOrEmpty(fileReRoute.ServiceName)
 | 
				
			||||||
 | 
					                && !string.IsNullOrEmpty(globalConfiguration?.ServiceDiscoveryProvider?.Provider);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var serviceProviderPort = globalConfiguration?.ServiceDiscoveryProvider?.Port ?? 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return new ServiceProviderConfigurationBuilder()
 | 
				
			||||||
 | 
					                    .WithServiceName(fileReRoute.ServiceName)
 | 
				
			||||||
 | 
					                    .WithDownstreamHost(fileReRoute.DownstreamHost)
 | 
				
			||||||
 | 
					                    .WithDownstreamPort(fileReRoute.DownstreamPort)
 | 
				
			||||||
 | 
					                    .WithUseServiceDiscovery(useServiceDiscovery)
 | 
				
			||||||
 | 
					                    .WithServiceDiscoveryProvider(globalConfiguration?.ServiceDiscoveryProvider?.Provider)
 | 
				
			||||||
 | 
					                    .WithServiceDiscoveryProviderHost(globalConfiguration?.ServiceDiscoveryProvider?.Host)
 | 
				
			||||||
 | 
					                    .WithServiceDiscoveryProviderPort(serviceProviderPort)
 | 
				
			||||||
 | 
					                    .Build();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,56 @@
 | 
				
			|||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					using Ocelot.Utilities;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Configuration.Creator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class UpstreamTemplatePatternCreator : IUpstreamTemplatePatternCreator
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private const string RegExMatchEverything = ".*";
 | 
				
			||||||
 | 
					        private const string RegExMatchEndString = "$";
 | 
				
			||||||
 | 
					        private const string RegExIgnoreCase = "(?i)";
 | 
				
			||||||
 | 
					        private const string RegExForwardSlashOnly = "^/$";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string Create(FileReRoute reRoute)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var upstreamTemplate = reRoute.UpstreamPathTemplate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            upstreamTemplate = upstreamTemplate.SetLastCharacterAs('/');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var placeholders = new List<string>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (var i = 0; i < upstreamTemplate.Length; i++)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (IsPlaceHolder(upstreamTemplate, i))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    var postitionOfPlaceHolderClosingBracket = upstreamTemplate.IndexOf('}', i);
 | 
				
			||||||
 | 
					                    var difference = postitionOfPlaceHolderClosingBracket - i + 1;
 | 
				
			||||||
 | 
					                    var variableName = upstreamTemplate.Substring(i, difference);
 | 
				
			||||||
 | 
					                    placeholders.Add(variableName);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            foreach (var placeholder in placeholders)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                upstreamTemplate = upstreamTemplate.Replace(placeholder, RegExMatchEverything);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (upstreamTemplate == "/")
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return RegExForwardSlashOnly;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var route = reRoute.ReRouteIsCaseSensitive 
 | 
				
			||||||
 | 
					                ? $"{upstreamTemplate}{RegExMatchEndString}" 
 | 
				
			||||||
 | 
					                : $"{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return route;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private bool IsPlaceHolder(string upstreamTemplate, int i)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return upstreamTemplate[i] == '{';
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -25,7 +25,7 @@ namespace Ocelot.Configuration
 | 
				
			|||||||
            string downstreamHost, 
 | 
					            string downstreamHost, 
 | 
				
			||||||
            int downstreamPort, 
 | 
					            int downstreamPort, 
 | 
				
			||||||
            string reRouteKey, 
 | 
					            string reRouteKey, 
 | 
				
			||||||
            ServiceProviderConfiguraion serviceProviderConfiguraion,
 | 
					            ServiceProviderConfiguration serviceProviderConfiguraion,
 | 
				
			||||||
            bool isQos,
 | 
					            bool isQos,
 | 
				
			||||||
            QoSOptions qos,
 | 
					            QoSOptions qos,
 | 
				
			||||||
            bool enableRateLimit,
 | 
					            bool enableRateLimit,
 | 
				
			||||||
@@ -81,7 +81,7 @@ namespace Ocelot.Configuration
 | 
				
			|||||||
        public string LoadBalancer {get;private set;}
 | 
					        public string LoadBalancer {get;private set;}
 | 
				
			||||||
        public string DownstreamHost { get; private set; }
 | 
					        public string DownstreamHost { get; private set; }
 | 
				
			||||||
        public int DownstreamPort { get; private set; }
 | 
					        public int DownstreamPort { get; private set; }
 | 
				
			||||||
        public ServiceProviderConfiguraion ServiceProviderConfiguraion { get; private set; }
 | 
					        public ServiceProviderConfiguration ServiceProviderConfiguraion { get; private set; }
 | 
				
			||||||
        public bool EnableEndpointRateLimiting { get; private set; }
 | 
					        public bool EnableEndpointRateLimiting { get; private set; }
 | 
				
			||||||
        public RateLimitOptions RateLimitOptions { get; private set; }
 | 
					        public RateLimitOptions RateLimitOptions { get; private set; }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										20
									
								
								src/Ocelot/Configuration/ReRouteOptions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/Ocelot/Configuration/ReRouteOptions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					namespace Ocelot.Configuration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class ReRouteOptions
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public ReRouteOptions(bool isAuthenticated, bool isAuthorised, bool isCached, bool isQos, bool isEnableRateLimiting)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            IsAuthenticated = isAuthenticated;
 | 
				
			||||||
 | 
					            IsAuthorised = isAuthorised;
 | 
				
			||||||
 | 
					            IsCached = isCached;
 | 
				
			||||||
 | 
					            IsQos = isQos;
 | 
				
			||||||
 | 
					            EnableRateLimiting = isEnableRateLimiting;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        public bool IsAuthenticated { get; private set; }
 | 
				
			||||||
 | 
					        public bool IsAuthorised { get; private set; }
 | 
				
			||||||
 | 
					        public bool IsCached { get; private set; }
 | 
				
			||||||
 | 
					        public bool IsQos { get; private set; }
 | 
				
			||||||
 | 
					        public bool EnableRateLimiting { get; private set; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
namespace Ocelot.Configuration
 | 
					namespace Ocelot.Configuration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class ServiceProviderConfiguraion
 | 
					    public class ServiceProviderConfiguration
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public ServiceProviderConfiguraion(string serviceName, string downstreamHost,
 | 
					        public ServiceProviderConfiguration(string serviceName, string downstreamHost,
 | 
				
			||||||
            int downstreamPort, bool useServiceDiscovery, string serviceDiscoveryProvider, string serviceProviderHost, int serviceProviderPort)
 | 
					            int downstreamPort, bool useServiceDiscovery, string serviceDiscoveryProvider, string serviceProviderHost, int serviceProviderPort)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            ServiceName = serviceName;
 | 
					            ServiceName = serviceName;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -60,6 +60,14 @@ namespace Ocelot.DependencyInjection
 | 
				
			|||||||
            services.AddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>();
 | 
					            services.AddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>();
 | 
				
			||||||
            services.AddSingleton<IConfigurationValidator, FileConfigurationValidator>();
 | 
					            services.AddSingleton<IConfigurationValidator, FileConfigurationValidator>();
 | 
				
			||||||
            services.AddSingleton<IBaseUrlFinder, BaseUrlFinder>();
 | 
					            services.AddSingleton<IBaseUrlFinder, BaseUrlFinder>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IClaimsToThingCreator, ClaimsToThingCreator>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IAuthenticationOptionsCreator, AuthenticationOptionsCreator>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IUpstreamTemplatePatternCreator, UpstreamTemplatePatternCreator>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IRequestIdKeyCreator, RequestIdKeyCreator>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IServiceProviderConfigurationCreator,ServiceProviderConfigurationCreator>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IQoSOptionsCreator, QoSOptionsCreator>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IReRouteOptionsCreator, ReRouteOptionsCreator>();
 | 
				
			||||||
 | 
					            services.AddSingleton<IRateLimitOptionsCreator, RateLimitOptionsCreator>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var identityServerConfiguration = IdentityServerConfigurationCreator.GetIdentityServerConfiguration();
 | 
					            var identityServerConfiguration = IdentityServerConfigurationCreator.GetIdentityServerConfiguration();
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,6 @@ namespace Ocelot.Request.Builder
 | 
				
			|||||||
            string downstreamUrl, 
 | 
					            string downstreamUrl, 
 | 
				
			||||||
            Stream content, 
 | 
					            Stream content, 
 | 
				
			||||||
            IHeaderDictionary headers,
 | 
					            IHeaderDictionary headers,
 | 
				
			||||||
            IRequestCookieCollection cookies, 
 | 
					 | 
				
			||||||
            QueryString queryString, 
 | 
					            QueryString queryString, 
 | 
				
			||||||
            string contentType, 
 | 
					            string contentType, 
 | 
				
			||||||
            RequestId.RequestId requestId,
 | 
					            RequestId.RequestId requestId,
 | 
				
			||||||
@@ -29,7 +28,6 @@ namespace Ocelot.Request.Builder
 | 
				
			|||||||
                .WithContentType(contentType)
 | 
					                .WithContentType(contentType)
 | 
				
			||||||
                .WithHeaders(headers)
 | 
					                .WithHeaders(headers)
 | 
				
			||||||
                .WithRequestId(requestId)
 | 
					                .WithRequestId(requestId)
 | 
				
			||||||
                .WithCookies(cookies)
 | 
					 | 
				
			||||||
                .WithIsQos(isQos)
 | 
					                .WithIsQos(isQos)
 | 
				
			||||||
                .WithQos(qosProvider)
 | 
					                .WithQos(qosProvider)
 | 
				
			||||||
                .Build();
 | 
					                .Build();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,7 +12,6 @@ namespace Ocelot.Request.Builder
 | 
				
			|||||||
            string downstreamUrl,
 | 
					            string downstreamUrl,
 | 
				
			||||||
            Stream content,
 | 
					            Stream content,
 | 
				
			||||||
            IHeaderDictionary headers,
 | 
					            IHeaderDictionary headers,
 | 
				
			||||||
            IRequestCookieCollection cookies,
 | 
					 | 
				
			||||||
            QueryString queryString,
 | 
					            QueryString queryString,
 | 
				
			||||||
            string contentType,
 | 
					            string contentType,
 | 
				
			||||||
            RequestId.RequestId requestId,
 | 
					            RequestId.RequestId requestId,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,6 @@ namespace Ocelot.Request.Builder
 | 
				
			|||||||
        private string _contentType;
 | 
					        private string _contentType;
 | 
				
			||||||
        private IHeaderDictionary _headers;
 | 
					        private IHeaderDictionary _headers;
 | 
				
			||||||
        private RequestId.RequestId _requestId;
 | 
					        private RequestId.RequestId _requestId;
 | 
				
			||||||
        private IRequestCookieCollection _cookies;
 | 
					 | 
				
			||||||
        private readonly string[] _unsupportedHeaders = {"host"};
 | 
					        private readonly string[] _unsupportedHeaders = {"host"};
 | 
				
			||||||
        private bool _isQos;
 | 
					        private bool _isQos;
 | 
				
			||||||
        private IQoSProvider _qoSProvider;
 | 
					        private IQoSProvider _qoSProvider;
 | 
				
			||||||
@@ -68,12 +67,6 @@ namespace Ocelot.Request.Builder
 | 
				
			|||||||
            return this;
 | 
					            return this;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public RequestBuilder WithCookies(IRequestCookieCollection cookies)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _cookies = cookies;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public RequestBuilder WithIsQos(bool isqos)
 | 
					        public RequestBuilder WithIsQos(bool isqos)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _isQos = isqos;
 | 
					            _isQos = isqos;
 | 
				
			||||||
@@ -103,9 +96,7 @@ namespace Ocelot.Request.Builder
 | 
				
			|||||||
                AddRequestIdHeader(_requestId, httpRequestMessage);
 | 
					                AddRequestIdHeader(_requestId, httpRequestMessage);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var cookieContainer = CreateCookieContainer(uri);
 | 
					            return new Request(httpRequestMessage,_isQos, _qoSProvider);
 | 
				
			||||||
 | 
					 | 
				
			||||||
            return new Request(httpRequestMessage, cookieContainer,_isQos, _qoSProvider);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private Uri CreateUri()
 | 
					        private Uri CreateUri()
 | 
				
			||||||
@@ -153,21 +144,6 @@ namespace Ocelot.Request.Builder
 | 
				
			|||||||
            return !_unsupportedHeaders.Contains(header.Key.ToLower());
 | 
					            return !_unsupportedHeaders.Contains(header.Key.ToLower());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private CookieContainer CreateCookieContainer(Uri uri)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var cookieContainer = new CookieContainer();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (_cookies != null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                foreach (var cookie in _cookies)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    cookieContainer.Add(uri, new Cookie(cookie.Key, cookie.Value));
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return cookieContainer;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void AddRequestIdHeader(RequestId.RequestId requestId, HttpRequestMessage httpRequestMessage)
 | 
					        private void AddRequestIdHeader(RequestId.RequestId requestId, HttpRequestMessage httpRequestMessage)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            httpRequestMessage.Headers.Add(requestId.RequestIdKey, requestId.RequestIdValue);
 | 
					            httpRequestMessage.Headers.Add(requestId.RequestIdKey, requestId.RequestIdValue);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,7 +48,6 @@ namespace Ocelot.Request.Middleware
 | 
				
			|||||||
                    DownstreamUrl,
 | 
					                    DownstreamUrl,
 | 
				
			||||||
                    context.Request.Body,
 | 
					                    context.Request.Body,
 | 
				
			||||||
                    context.Request.Headers,
 | 
					                    context.Request.Headers,
 | 
				
			||||||
                    context.Request.Cookies,
 | 
					 | 
				
			||||||
                    context.Request.QueryString,
 | 
					                    context.Request.QueryString,
 | 
				
			||||||
                    context.Request.ContentType,
 | 
					                    context.Request.ContentType,
 | 
				
			||||||
                    new RequestId.RequestId(DownstreamRoute?.ReRoute?.RequestIdKey, context.TraceIdentifier),
 | 
					                    new RequestId.RequestId(DownstreamRoute?.ReRoute?.RequestIdKey, context.TraceIdentifier),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,4 @@
 | 
				
			|||||||
using Ocelot.Configuration;
 | 
					using System.Net.Http;
 | 
				
			||||||
using Ocelot.Values;
 | 
					 | 
				
			||||||
using System.Net;
 | 
					 | 
				
			||||||
using System.Net.Http;
 | 
					 | 
				
			||||||
using Ocelot.Requester.QoS;
 | 
					using Ocelot.Requester.QoS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.Request
 | 
					namespace Ocelot.Request
 | 
				
			||||||
@@ -10,18 +7,15 @@ namespace Ocelot.Request
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        public Request(
 | 
					        public Request(
 | 
				
			||||||
            HttpRequestMessage httpRequestMessage, 
 | 
					            HttpRequestMessage httpRequestMessage, 
 | 
				
			||||||
            CookieContainer cookieContainer,
 | 
					 | 
				
			||||||
            bool isQos,
 | 
					            bool isQos,
 | 
				
			||||||
            IQoSProvider qosProvider)
 | 
					            IQoSProvider qosProvider)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            HttpRequestMessage = httpRequestMessage;
 | 
					            HttpRequestMessage = httpRequestMessage;
 | 
				
			||||||
            CookieContainer = cookieContainer;
 | 
					 | 
				
			||||||
            IsQos = isQos;
 | 
					            IsQos = isQos;
 | 
				
			||||||
            QosProvider = qosProvider;
 | 
					            QosProvider = qosProvider;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public HttpRequestMessage HttpRequestMessage { get; private set; }
 | 
					        public HttpRequestMessage HttpRequestMessage { get; private set; }
 | 
				
			||||||
        public CookieContainer CookieContainer { get; private set; }
 | 
					 | 
				
			||||||
        public bool IsQos { get; private set; }
 | 
					        public bool IsQos { get; private set; }
 | 
				
			||||||
        public IQoSProvider QosProvider { get; private set; }
 | 
					        public IQoSProvider QosProvider { get; private set; }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,14 +14,14 @@ namespace Ocelot.Requester
 | 
				
			|||||||
        private readonly Dictionary<int, Func<DelegatingHandler>> _handlers = new Dictionary<int, Func<DelegatingHandler>>();
 | 
					        private readonly Dictionary<int, Func<DelegatingHandler>> _handlers = new Dictionary<int, Func<DelegatingHandler>>();
 | 
				
			||||||
        private Dictionary<string, string> _defaultHeaders;
 | 
					        private Dictionary<string, string> _defaultHeaders;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public  IHttpClientBuilder WithQos(IQoSProvider qosProvider, IOcelotLogger logger)
 | 
					        public  IHttpClientBuilder WithQos(IQoSProvider qosProvider, IOcelotLogger logger)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _handlers.Add(5000, () => new PollyCircuitBreakingDelegatingHandler(qosProvider, logger));
 | 
					            _handlers.Add(5000, () => new PollyCircuitBreakingDelegatingHandler(qosProvider, logger));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return this;
 | 
					            return this;
 | 
				
			||||||
 | 
					 | 
				
			||||||
        }  
 | 
					        }  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
        public IHttpClient Create()
 | 
					        public IHttpClient Create()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var httpclientHandler = new HttpClientHandler();
 | 
					            var httpclientHandler = new HttpClientHandler();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -55,7 +55,7 @@ namespace Ocelot.Requester
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private string GetCacheKey(Request.Request request, HttpClientBuilder builder)
 | 
					        private string GetCacheKey(Request.Request request, IHttpClientBuilder builder)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            string baseUrl = $"{request.HttpRequestMessage.RequestUri.Scheme}://{request.HttpRequestMessage.RequestUri.Authority}";
 | 
					            string baseUrl = $"{request.HttpRequestMessage.RequestUri.Scheme}://{request.HttpRequestMessage.RequestUri.Authority}";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,9 @@
 | 
				
			|||||||
using System;
 | 
					 | 
				
			||||||
using Ocelot.Configuration;
 | 
					using Ocelot.Configuration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.ServiceDiscovery
 | 
					namespace Ocelot.ServiceDiscovery
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public interface IServiceDiscoveryProviderFactory
 | 
					    public interface IServiceDiscoveryProviderFactory
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        IServiceDiscoveryProvider Get(ServiceProviderConfiguraion serviceConfig);
 | 
					        IServiceDiscoveryProvider Get(ServiceProviderConfiguration serviceConfig);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -6,7 +6,7 @@ namespace Ocelot.ServiceDiscovery
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public class ServiceDiscoveryProviderFactory : IServiceDiscoveryProviderFactory
 | 
					    public class ServiceDiscoveryProviderFactory : IServiceDiscoveryProviderFactory
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public  IServiceDiscoveryProvider Get(ServiceProviderConfiguraion serviceConfig)
 | 
					        public  IServiceDiscoveryProvider Get(ServiceProviderConfiguration serviceConfig)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (serviceConfig.UseServiceDiscovery)
 | 
					            if (serviceConfig.UseServiceDiscovery)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,7 @@ using Shouldly;
 | 
				
			|||||||
using TestStack.BDDfy;
 | 
					using TestStack.BDDfy;
 | 
				
			||||||
using Xunit;
 | 
					using Xunit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[assembly: CollectionBehavior(DisableTestParallelization = true)]
 | 
				
			||||||
namespace Ocelot.IntegrationTests
 | 
					namespace Ocelot.IntegrationTests
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class AdministrationTests : IDisposable
 | 
					    public class AdministrationTests : IDisposable
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										190
									
								
								test/Ocelot.IntegrationTests/ThreadSafeHeadersTests.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								test/Ocelot.IntegrationTests/ThreadSafeHeadersTests.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,190 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.IO;
 | 
				
			||||||
 | 
					using System.Net;
 | 
				
			||||||
 | 
					using System.Net.Http;
 | 
				
			||||||
 | 
					using System.Net.Http.Headers;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Builder;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Hosting;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.DependencyInjection;
 | 
				
			||||||
 | 
					using Newtonsoft.Json;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					using Ocelot.ManualTest;
 | 
				
			||||||
 | 
					using Shouldly;
 | 
				
			||||||
 | 
					using TestStack.BDDfy;
 | 
				
			||||||
 | 
					using Xunit;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using System.Threading;
 | 
				
			||||||
 | 
					using System.Collections.Concurrent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.IntegrationTests
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class ThreadSafeHeadersTests : IDisposable
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private readonly HttpClient _httpClient;
 | 
				
			||||||
 | 
					        private HttpResponseMessage _response;
 | 
				
			||||||
 | 
					        private IWebHost _builder;
 | 
				
			||||||
 | 
					        private IWebHostBuilder _webHostBuilder;
 | 
				
			||||||
 | 
					        private readonly string _ocelotBaseUrl;
 | 
				
			||||||
 | 
					        private BearerToken _token;
 | 
				
			||||||
 | 
					        private IWebHost _downstreamBuilder;
 | 
				
			||||||
 | 
					        private readonly Random _random;
 | 
				
			||||||
 | 
					        private readonly ConcurrentBag<ThreadSafeHeadersTestResult> _results;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ThreadSafeHeadersTests()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _results = new ConcurrentBag<ThreadSafeHeadersTestResult>();
 | 
				
			||||||
 | 
					            _random = new Random();
 | 
				
			||||||
 | 
					            _httpClient = new HttpClient();
 | 
				
			||||||
 | 
					            _ocelotBaseUrl = "http://localhost:5001";
 | 
				
			||||||
 | 
					            _httpClient.BaseAddress = new Uri(_ocelotBaseUrl);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_return_same_response_for_each_different_header_under_load_to_downsteam_service()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var configuration = new FileConfiguration
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ReRoutes = new List<FileReRoute>
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        new FileReRoute
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            DownstreamPathTemplate = "/",
 | 
				
			||||||
 | 
					                            DownstreamScheme = "http",
 | 
				
			||||||
 | 
					                            DownstreamHost = "localhost",
 | 
				
			||||||
 | 
					                            DownstreamPort = 51879,
 | 
				
			||||||
 | 
					                            UpstreamPathTemplate = "/",
 | 
				
			||||||
 | 
					                            UpstreamHttpMethod = "Get",
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(x => GivenThereIsAConfiguration(configuration))
 | 
				
			||||||
 | 
					               .And(x => GivenThereIsAServiceRunningOn("http://localhost:51879"))
 | 
				
			||||||
 | 
					                .And(x => GivenOcelotIsRunning())
 | 
				
			||||||
 | 
					                .When(x => WhenIGetUrlOnTheApiGatewayMultipleTimesWithDifferentHeaderValues("/", 300))
 | 
				
			||||||
 | 
					                .Then(x => ThenTheSameHeaderValuesAreReturnedByTheDownstreamService())
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenThereIsAServiceRunningOn(string url)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _downstreamBuilder = new WebHostBuilder()
 | 
				
			||||||
 | 
					                .UseUrls(url)
 | 
				
			||||||
 | 
					                .UseKestrel()
 | 
				
			||||||
 | 
					                .UseContentRoot(Directory.GetCurrentDirectory())
 | 
				
			||||||
 | 
					                .UseIISIntegration()
 | 
				
			||||||
 | 
					                .UseUrls(url)
 | 
				
			||||||
 | 
					                .Configure(app =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    app.Run(async context =>
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        var header = context.Request.Headers["ThreadSafeHeadersTest"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        context.Response.StatusCode = 200;
 | 
				
			||||||
 | 
					                        await context.Response.WriteAsync(header[0]);
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _downstreamBuilder.Start();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenOcelotIsRunning()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _webHostBuilder = new WebHostBuilder()
 | 
				
			||||||
 | 
					                .UseUrls(_ocelotBaseUrl)
 | 
				
			||||||
 | 
					                .UseKestrel()
 | 
				
			||||||
 | 
					                .UseContentRoot(Directory.GetCurrentDirectory())
 | 
				
			||||||
 | 
					                .ConfigureServices(x =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    x.AddSingleton(_webHostBuilder);
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .UseStartup<Startup>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _builder = _webHostBuilder.Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _builder.Start();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var configurationPath = $"{Directory.GetCurrentDirectory()}/configuration.json";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (File.Exists(configurationPath))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                File.Delete(configurationPath);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            File.WriteAllText(configurationPath, jsonConfiguration);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var text = File.ReadAllText(configurationPath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            configurationPath = $"{AppContext.BaseDirectory}/configuration.json";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (File.Exists(configurationPath))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                File.Delete(configurationPath);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            File.WriteAllText(configurationPath, jsonConfiguration);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            text = File.ReadAllText(configurationPath);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void WhenIGetUrlOnTheApiGatewayMultipleTimesWithDifferentHeaderValues(string url, int times)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var tasks = new Task[times];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (int i = 0; i < times; i++)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var urlCopy = url;
 | 
				
			||||||
 | 
					                var random = _random.Next(0, 50);
 | 
				
			||||||
 | 
					                tasks[i] = GetForThreadSafeHeadersTest(urlCopy, random);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Task.WaitAll(tasks);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private async Task GetForThreadSafeHeadersTest(string url, int random)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var request = new HttpRequestMessage(HttpMethod.Get, url);
 | 
				
			||||||
 | 
					            request.Headers.Add("ThreadSafeHeadersTest", new List<string> { random.ToString() });
 | 
				
			||||||
 | 
					            var response = await _httpClient.SendAsync(request);
 | 
				
			||||||
 | 
					            var content = await response.Content.ReadAsStringAsync();
 | 
				
			||||||
 | 
					            int result = int.Parse(content);
 | 
				
			||||||
 | 
					            var tshtr = new ThreadSafeHeadersTestResult(result, random);
 | 
				
			||||||
 | 
					            _results.Add(tshtr);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void ThenTheSameHeaderValuesAreReturnedByTheDownstreamService()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            foreach(var result in _results)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                result.Result.ShouldBe(result.Random);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        public void Dispose()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _builder?.Dispose();
 | 
				
			||||||
 | 
					            _httpClient?.Dispose();
 | 
				
			||||||
 | 
					            _downstreamBuilder?.Dispose();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        class ThreadSafeHeadersTestResult
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            public ThreadSafeHeadersTestResult(int result, int random)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Result = result;
 | 
				
			||||||
 | 
					                Random = random;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            public int Result { get; private set; }
 | 
				
			||||||
 | 
					            public int Random { get; private set; }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1 +1 @@
 | 
				
			|||||||
{"ReRoutes":[],"GlobalConfiguration":{"RequestIdKey":null,"ServiceDiscoveryProvider":{"Provider":null,"Host":null,"Port":0},"AdministrationPath":"/administration","RateLimitOptions":{"ClientIdHeader":"ClientId","QuotaExceededMessage":null,"RateLimitCounterPrefix":"ocelot","DisableRateLimitHeaders":false,"HttpStatusCode":429}}}
 | 
					{"ReRoutes":[{"DownstreamPathTemplate":"/","UpstreamPathTemplate":"/","UpstreamHttpMethod":"Get","AuthenticationOptions":{"Provider":null,"ProviderRootUrl":null,"ScopeName":null,"RequireHttps":false,"AdditionalScopes":[],"ScopeSecret":null},"AddHeadersToRequest":{},"AddClaimsToRequest":{},"RouteClaimsRequirement":{},"AddQueriesToRequest":{},"RequestIdKey":null,"FileCacheOptions":{"TtlSeconds":0},"ReRouteIsCaseSensitive":false,"ServiceName":null,"DownstreamScheme":"http","DownstreamHost":"localhost","DownstreamPort":51879,"QoSOptions":{"ExceptionsAllowedBeforeBreaking":0,"DurationOfBreak":0,"TimeoutValue":0},"LoadBalancer":null,"RateLimitOptions":{"ClientWhitelist":[],"EnableRateLimiting":false,"Period":null,"PeriodTimespan":0.0,"Limit":0}}],"GlobalConfiguration":{"RequestIdKey":null,"ServiceDiscoveryProvider":{"Provider":null,"Host":null,"Port":0},"AdministrationPath":null,"RateLimitOptions":{"ClientIdHeader":"ClientId","QuotaExceededMessage":null,"RateLimitCounterPrefix":"ocelot","DisableRateLimitHeaders":false,"HttpStatusCode":429}}}
 | 
				
			||||||
@@ -1,303 +1,311 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "ReRoutes": [
 | 
					    "ReRoutes": [
 | 
				
			||||||
    {
 | 
					        {
 | 
				
			||||||
      "DownstreamPathTemplate": "/",
 | 
					            "DownstreamPathTemplate": "/",
 | 
				
			||||||
      "DownstreamScheme": "http",
 | 
					            "DownstreamScheme": "http",
 | 
				
			||||||
      "DownstreamHost": "localhost",
 | 
					            "DownstreamHost": "localhost",
 | 
				
			||||||
      "DownstreamPort": 52876,
 | 
					            "DownstreamPort": 52876,
 | 
				
			||||||
      "UpstreamPathTemplate": "/identityserverexample",
 | 
					            "UpstreamPathTemplate": "/identityserverexample",
 | 
				
			||||||
      "UpstreamHttpMethod": "Get",
 | 
					            "UpstreamHttpMethod": "Get",
 | 
				
			||||||
      "QoSOptions": {
 | 
					            "QoSOptions": {
 | 
				
			||||||
        "ExceptionsAllowedBeforeBreaking": 3,
 | 
					                "ExceptionsAllowedBeforeBreaking": 3,
 | 
				
			||||||
        "DurationOfBreak": 10,
 | 
					                "DurationOfBreak": 10,
 | 
				
			||||||
        "TimeoutValue": 5000
 | 
					                "TimeoutValue": 5000
 | 
				
			||||||
      },
 | 
					            },
 | 
				
			||||||
      "AuthenticationOptions": {
 | 
					            "AuthenticationOptions": {
 | 
				
			||||||
        "Provider": "IdentityServer",
 | 
					                "Provider": "IdentityServer",
 | 
				
			||||||
        "ProviderRootUrl": "http://localhost:52888",
 | 
					                "ProviderRootUrl": "http://localhost:52888",
 | 
				
			||||||
        "ScopeName": "api",
 | 
					                "ScopeName": "api",
 | 
				
			||||||
        "AdditionalScopes": [
 | 
					                "AdditionalScopes": [
 | 
				
			||||||
          "openid",
 | 
					                    "openid",
 | 
				
			||||||
          "offline_access"
 | 
					                    "offline_access"
 | 
				
			||||||
        ],
 | 
					                ],
 | 
				
			||||||
        "ScopeSecret": "secret"
 | 
					                "ScopeSecret": "secret"
 | 
				
			||||||
      },
 | 
					            },
 | 
				
			||||||
      "AddHeadersToRequest": {
 | 
					            "AddHeadersToRequest": {
 | 
				
			||||||
        "CustomerId": "Claims[CustomerId] > value",
 | 
					                "CustomerId": "Claims[CustomerId] > value",
 | 
				
			||||||
        "LocationId": "Claims[LocationId] > value",
 | 
					                "LocationId": "Claims[LocationId] > value",
 | 
				
			||||||
        "UserType": "Claims[sub] > value[0] > |",
 | 
					                "UserType": "Claims[sub] > value[0] > |",
 | 
				
			||||||
        "UserId": "Claims[sub] > value[1] > |"
 | 
					                "UserId": "Claims[sub] > value[1] > |"
 | 
				
			||||||
      },
 | 
					            },
 | 
				
			||||||
      "AddClaimsToRequest": {
 | 
					            "AddClaimsToRequest": {
 | 
				
			||||||
        "CustomerId": "Claims[CustomerId] > value",
 | 
					                "CustomerId": "Claims[CustomerId] > value",
 | 
				
			||||||
        "LocationId": "Claims[LocationId] > value",
 | 
					                "LocationId": "Claims[LocationId] > value",
 | 
				
			||||||
        "UserType": "Claims[sub] > value[0] > |",
 | 
					                "UserType": "Claims[sub] > value[0] > |",
 | 
				
			||||||
        "UserId": "Claims[sub] > value[1] > |"
 | 
					                "UserId": "Claims[sub] > value[1] > |"
 | 
				
			||||||
      },
 | 
					            },
 | 
				
			||||||
      "AddQueriesToRequest": {
 | 
					            "AddQueriesToRequest": {
 | 
				
			||||||
        "CustomerId": "Claims[CustomerId] > value",
 | 
					                "CustomerId": "Claims[CustomerId] > value",
 | 
				
			||||||
        "LocationId": "Claims[LocationId] > value",
 | 
					                "LocationId": "Claims[LocationId] > value",
 | 
				
			||||||
        "UserType": "Claims[sub] > value[0] > |",
 | 
					                "UserType": "Claims[sub] > value[0] > |",
 | 
				
			||||||
        "UserId": "Claims[sub] > value[1] > |"
 | 
					                "UserId": "Claims[sub] > value[1] > |"
 | 
				
			||||||
      },
 | 
					            },
 | 
				
			||||||
      "RouteClaimsRequirement": {
 | 
					            "RouteClaimsRequirement": {
 | 
				
			||||||
        "UserType": "registered"
 | 
					                "UserType": "registered"
 | 
				
			||||||
      },
 | 
					            },
 | 
				
			||||||
      "RequestIdKey": "OcRequestId"
 | 
					            "RequestIdKey": "OcRequestId"
 | 
				
			||||||
    },
 | 
					        },
 | 
				
			||||||
    {
 | 
					        {
 | 
				
			||||||
      "DownstreamPathTemplate": "/posts",
 | 
					            "DownstreamPathTemplate": "/posts",
 | 
				
			||||||
      "DownstreamScheme": "https",
 | 
					            "DownstreamScheme": "https",
 | 
				
			||||||
      "DownstreamHost": "jsonplaceholder.typicode.com",
 | 
					            "DownstreamHost": "jsonplaceholder.typicode.com",
 | 
				
			||||||
      "DownstreamPort": 443,
 | 
					            "DownstreamPort": 443,
 | 
				
			||||||
      "UpstreamPathTemplate": "/posts",
 | 
					            "UpstreamPathTemplate": "/posts",
 | 
				
			||||||
      "UpstreamHttpMethod": "Get",
 | 
					            "UpstreamHttpMethod": "Get",
 | 
				
			||||||
      "QoSOptions": {
 | 
					            "QoSOptions": {
 | 
				
			||||||
        "ExceptionsAllowedBeforeBreaking": 3,
 | 
					                "ExceptionsAllowedBeforeBreaking": 3,
 | 
				
			||||||
        "DurationOfBreak": 10,
 | 
					                "DurationOfBreak": 10,
 | 
				
			||||||
        "TimeoutValue": 5000
 | 
					                "TimeoutValue": 5000
 | 
				
			||||||
      }
 | 
					            }
 | 
				
			||||||
    },
 | 
					        },
 | 
				
			||||||
    {
 | 
					        {
 | 
				
			||||||
      "DownstreamPathTemplate": "/posts/{postId}",
 | 
					            "DownstreamPathTemplate": "/posts/{postId}",
 | 
				
			||||||
      "DownstreamScheme": "http",
 | 
					            "DownstreamScheme": "http",
 | 
				
			||||||
      "DownstreamHost": "jsonplaceholder.typicode.com",
 | 
					            "DownstreamHost": "jsonplaceholder.typicode.com",
 | 
				
			||||||
      "DownstreamPort": 80,
 | 
					            "DownstreamPort": 80,
 | 
				
			||||||
      "UpstreamPathTemplate": "/posts/{postId}",
 | 
					            "UpstreamPathTemplate": "/posts/{postId}",
 | 
				
			||||||
      "UpstreamHttpMethod": "Get",
 | 
					            "UpstreamHttpMethod": "Get",
 | 
				
			||||||
      "QoSOptions": {
 | 
					            "QoSOptions": {
 | 
				
			||||||
        "ExceptionsAllowedBeforeBreaking": 3,
 | 
					                "ExceptionsAllowedBeforeBreaking": 3,
 | 
				
			||||||
        "DurationOfBreak": 10,
 | 
					                "DurationOfBreak": 10,
 | 
				
			||||||
        "TimeoutValue": 5000
 | 
					                "TimeoutValue": 5000
 | 
				
			||||||
      }
 | 
					            }
 | 
				
			||||||
    },
 | 
					        },
 | 
				
			||||||
    {
 | 
					        {
 | 
				
			||||||
      "DownstreamPathTemplate": "/posts/{postId}/comments",
 | 
					            "DownstreamPathTemplate": "/posts/{postId}/comments",
 | 
				
			||||||
      "DownstreamScheme": "http",
 | 
					            "DownstreamScheme": "http",
 | 
				
			||||||
      "DownstreamHost": "jsonplaceholder.typicode.com",
 | 
					            "DownstreamHost": "jsonplaceholder.typicode.com",
 | 
				
			||||||
      "DownstreamPort": 80,
 | 
					            "DownstreamPort": 80,
 | 
				
			||||||
      "UpstreamPathTemplate": "/posts/{postId}/comments",
 | 
					            "UpstreamPathTemplate": "/posts/{postId}/comments",
 | 
				
			||||||
      "UpstreamHttpMethod": "Get",
 | 
					            "UpstreamHttpMethod": "Get",
 | 
				
			||||||
      "QoSOptions": {
 | 
					            "QoSOptions": {
 | 
				
			||||||
        "ExceptionsAllowedBeforeBreaking": 3,
 | 
					                "ExceptionsAllowedBeforeBreaking": 3,
 | 
				
			||||||
        "DurationOfBreak": 10,
 | 
					                "DurationOfBreak": 10,
 | 
				
			||||||
        "TimeoutValue": 5000
 | 
					                "TimeoutValue": 5000
 | 
				
			||||||
      }
 | 
					            }
 | 
				
			||||||
    },
 | 
					        },
 | 
				
			||||||
    {
 | 
					        {
 | 
				
			||||||
      "DownstreamPathTemplate": "/comments",
 | 
					            "DownstreamPathTemplate": "/comments",
 | 
				
			||||||
      "DownstreamScheme": "http",
 | 
					            "DownstreamScheme": "http",
 | 
				
			||||||
      "DownstreamHost": "jsonplaceholder.typicode.com",
 | 
					            "DownstreamHost": "jsonplaceholder.typicode.com",
 | 
				
			||||||
      "DownstreamPort": 80,
 | 
					            "DownstreamPort": 80,
 | 
				
			||||||
      "UpstreamPathTemplate": "/comments",
 | 
					            "UpstreamPathTemplate": "/comments",
 | 
				
			||||||
      "UpstreamHttpMethod": "Get",
 | 
					            "UpstreamHttpMethod": "Get",
 | 
				
			||||||
      "QoSOptions": {
 | 
					            "QoSOptions": {
 | 
				
			||||||
        "ExceptionsAllowedBeforeBreaking": 3,
 | 
					                "ExceptionsAllowedBeforeBreaking": 3,
 | 
				
			||||||
        "DurationOfBreak": 10,
 | 
					                "DurationOfBreak": 10,
 | 
				
			||||||
        "TimeoutValue": 5000
 | 
					                "TimeoutValue": 5000
 | 
				
			||||||
      }
 | 
					            }
 | 
				
			||||||
    },
 | 
					        },
 | 
				
			||||||
    {
 | 
					        {
 | 
				
			||||||
      "DownstreamPathTemplate": "/posts",
 | 
					            "DownstreamPathTemplate": "/posts",
 | 
				
			||||||
      "DownstreamScheme": "http",
 | 
					            "DownstreamScheme": "http",
 | 
				
			||||||
      "DownstreamHost": "jsonplaceholder.typicode.com",
 | 
					            "DownstreamHost": "jsonplaceholder.typicode.com",
 | 
				
			||||||
      "DownstreamPort": 80,
 | 
					            "DownstreamPort": 80,
 | 
				
			||||||
      "UpstreamPathTemplate": "/posts",
 | 
					            "UpstreamPathTemplate": "/posts",
 | 
				
			||||||
      "UpstreamHttpMethod": "Post",
 | 
					            "UpstreamHttpMethod": "Post",
 | 
				
			||||||
      "QoSOptions": {
 | 
					            "QoSOptions": {
 | 
				
			||||||
        "ExceptionsAllowedBeforeBreaking": 3,
 | 
					                "ExceptionsAllowedBeforeBreaking": 3,
 | 
				
			||||||
        "DurationOfBreak": 10,
 | 
					                "DurationOfBreak": 10,
 | 
				
			||||||
        "TimeoutValue": 5000
 | 
					                "TimeoutValue": 5000
 | 
				
			||||||
      }
 | 
					            }
 | 
				
			||||||
    },
 | 
					        },
 | 
				
			||||||
    {
 | 
					        {
 | 
				
			||||||
      "DownstreamPathTemplate": "/posts/{postId}",
 | 
					            "DownstreamPathTemplate": "/posts/{postId}",
 | 
				
			||||||
      "DownstreamScheme": "http",
 | 
					            "DownstreamScheme": "http",
 | 
				
			||||||
      "DownstreamHost": "jsonplaceholder.typicode.com",
 | 
					            "DownstreamHost": "jsonplaceholder.typicode.com",
 | 
				
			||||||
      "DownstreamPort": 80,
 | 
					            "DownstreamPort": 80,
 | 
				
			||||||
      "UpstreamPathTemplate": "/posts/{postId}",
 | 
					            "UpstreamPathTemplate": "/posts/{postId}",
 | 
				
			||||||
      "UpstreamHttpMethod": "Put",
 | 
					            "UpstreamHttpMethod": "Put",
 | 
				
			||||||
      "QoSOptions": {
 | 
					            "QoSOptions": {
 | 
				
			||||||
        "ExceptionsAllowedBeforeBreaking": 3,
 | 
					                "ExceptionsAllowedBeforeBreaking": 3,
 | 
				
			||||||
        "DurationOfBreak": 10,
 | 
					                "DurationOfBreak": 10,
 | 
				
			||||||
        "TimeoutValue": 5000
 | 
					                "TimeoutValue": 5000
 | 
				
			||||||
      }
 | 
					            }
 | 
				
			||||||
    },
 | 
					        },
 | 
				
			||||||
    {
 | 
					        {
 | 
				
			||||||
      "DownstreamPathTemplate": "/posts/{postId}",
 | 
					            "DownstreamPathTemplate": "/posts/{postId}",
 | 
				
			||||||
      "DownstreamScheme": "http",
 | 
					            "DownstreamScheme": "http",
 | 
				
			||||||
      "DownstreamHost": "jsonplaceholder.typicode.com",
 | 
					            "DownstreamHost": "jsonplaceholder.typicode.com",
 | 
				
			||||||
      "DownstreamPort": 80,
 | 
					            "DownstreamPort": 80,
 | 
				
			||||||
      "UpstreamPathTemplate": "/posts/{postId}",
 | 
					            "UpstreamPathTemplate": "/posts/{postId}",
 | 
				
			||||||
      "UpstreamHttpMethod": "Patch",
 | 
					            "UpstreamHttpMethod": "Patch",
 | 
				
			||||||
      "QoSOptions": {
 | 
					            "QoSOptions": {
 | 
				
			||||||
        "ExceptionsAllowedBeforeBreaking": 3,
 | 
					                "ExceptionsAllowedBeforeBreaking": 3,
 | 
				
			||||||
        "DurationOfBreak": 10,
 | 
					                "DurationOfBreak": 10,
 | 
				
			||||||
        "TimeoutValue": 5000
 | 
					                "TimeoutValue": 5000
 | 
				
			||||||
      }
 | 
					            }
 | 
				
			||||||
    },
 | 
					        },
 | 
				
			||||||
    {
 | 
					        {
 | 
				
			||||||
      "DownstreamPathTemplate": "/posts/{postId}",
 | 
					            "DownstreamPathTemplate": "/posts/{postId}",
 | 
				
			||||||
      "DownstreamScheme": "http",
 | 
					            "DownstreamScheme": "http",
 | 
				
			||||||
      "DownstreamHost": "jsonplaceholder.typicode.com",
 | 
					            "DownstreamHost": "jsonplaceholder.typicode.com",
 | 
				
			||||||
      "DownstreamPort": 80,
 | 
					            "DownstreamPort": 80,
 | 
				
			||||||
      "UpstreamPathTemplate": "/posts/{postId}",
 | 
					            "UpstreamPathTemplate": "/posts/{postId}",
 | 
				
			||||||
      "UpstreamHttpMethod": "Delete",
 | 
					            "UpstreamHttpMethod": "Delete",
 | 
				
			||||||
      "QoSOptions": {
 | 
					            "QoSOptions": {
 | 
				
			||||||
        "ExceptionsAllowedBeforeBreaking": 3,
 | 
					                "ExceptionsAllowedBeforeBreaking": 3,
 | 
				
			||||||
        "DurationOfBreak": 10,
 | 
					                "DurationOfBreak": 10,
 | 
				
			||||||
        "TimeoutValue": 5000
 | 
					                "TimeoutValue": 5000
 | 
				
			||||||
      }
 | 
					            }
 | 
				
			||||||
    },
 | 
					        },
 | 
				
			||||||
    {
 | 
					        {
 | 
				
			||||||
      "DownstreamPathTemplate": "/api/products",
 | 
					            "DownstreamPathTemplate": "/api/products",
 | 
				
			||||||
      "DownstreamScheme": "http",
 | 
					            "DownstreamScheme": "http",
 | 
				
			||||||
      "DownstreamHost": "jsonplaceholder.typicode.com",
 | 
					            "DownstreamHost": "jsonplaceholder.typicode.com",
 | 
				
			||||||
      "DownstreamPort": 80,
 | 
					            "DownstreamPort": 80,
 | 
				
			||||||
      "UpstreamPathTemplate": "/products",
 | 
					            "UpstreamPathTemplate": "/products",
 | 
				
			||||||
      "UpstreamHttpMethod": "Get",
 | 
					            "UpstreamHttpMethod": "Get",
 | 
				
			||||||
      "QoSOptions": {
 | 
					            "QoSOptions": {
 | 
				
			||||||
        "ExceptionsAllowedBeforeBreaking": 3,
 | 
					                "ExceptionsAllowedBeforeBreaking": 3,
 | 
				
			||||||
        "DurationOfBreak": 10,
 | 
					                "DurationOfBreak": 10,
 | 
				
			||||||
        "TimeoutValue": 5000
 | 
					                "TimeoutValue": 5000
 | 
				
			||||||
      },
 | 
					            },
 | 
				
			||||||
      "FileCacheOptions": { "TtlSeconds": 15 }
 | 
					            "FileCacheOptions": { "TtlSeconds": 15 }
 | 
				
			||||||
    },
 | 
					        },
 | 
				
			||||||
    {
 | 
					        {
 | 
				
			||||||
      "DownstreamPathTemplate": "/api/products/{productId}",
 | 
					            "DownstreamPathTemplate": "/api/products/{productId}",
 | 
				
			||||||
      "DownstreamScheme": "http",
 | 
					            "DownstreamScheme": "http",
 | 
				
			||||||
      "DownstreamHost": "jsonplaceholder.typicode.com",
 | 
					            "DownstreamHost": "jsonplaceholder.typicode.com",
 | 
				
			||||||
      "DownstreamPort": 80,
 | 
					            "DownstreamPort": 80,
 | 
				
			||||||
      "UpstreamPathTemplate": "/products/{productId}",
 | 
					            "UpstreamPathTemplate": "/products/{productId}",
 | 
				
			||||||
      "UpstreamHttpMethod": "Get",
 | 
					            "UpstreamHttpMethod": "Get",
 | 
				
			||||||
      "FileCacheOptions": { "TtlSeconds": 15 }
 | 
					            "FileCacheOptions": { "TtlSeconds": 15 }
 | 
				
			||||||
    },
 | 
					        },
 | 
				
			||||||
    {
 | 
					        {
 | 
				
			||||||
      "DownstreamPathTemplate": "/api/products",
 | 
					            "DownstreamPathTemplate": "/api/products",
 | 
				
			||||||
      "DownstreamScheme": "http",
 | 
					            "DownstreamScheme": "http",
 | 
				
			||||||
      "DownstreamHost": "products20161126090340.azurewebsites.net",
 | 
					            "DownstreamHost": "products20161126090340.azurewebsites.net",
 | 
				
			||||||
      "DownstreamPort": 80,
 | 
					            "DownstreamPort": 80,
 | 
				
			||||||
      "UpstreamPathTemplate": "/products",
 | 
					            "UpstreamPathTemplate": "/products",
 | 
				
			||||||
      "UpstreamHttpMethod": "Post",
 | 
					            "UpstreamHttpMethod": "Post",
 | 
				
			||||||
      "QoSOptions": {
 | 
					            "QoSOptions": {
 | 
				
			||||||
        "ExceptionsAllowedBeforeBreaking": 3,
 | 
					                "ExceptionsAllowedBeforeBreaking": 3,
 | 
				
			||||||
        "DurationOfBreak": 10,
 | 
					                "DurationOfBreak": 10,
 | 
				
			||||||
        "TimeoutValue": 5000
 | 
					                "TimeoutValue": 5000
 | 
				
			||||||
      }
 | 
					            }
 | 
				
			||||||
    },
 | 
					        },
 | 
				
			||||||
    {
 | 
					        {
 | 
				
			||||||
      "DownstreamPathTemplate": "/api/products/{productId}",
 | 
					            "DownstreamPathTemplate": "/api/products/{productId}",
 | 
				
			||||||
      "DownstreamScheme": "http",
 | 
					            "DownstreamScheme": "http",
 | 
				
			||||||
      "DownstreamHost": "products20161126090340.azurewebsites.net",
 | 
					            "DownstreamHost": "products20161126090340.azurewebsites.net",
 | 
				
			||||||
      "DownstreamPort": 80,
 | 
					            "DownstreamPort": 80,
 | 
				
			||||||
      "UpstreamPathTemplate": "/products/{productId}",
 | 
					            "UpstreamPathTemplate": "/products/{productId}",
 | 
				
			||||||
      "UpstreamHttpMethod": "Put",
 | 
					            "UpstreamHttpMethod": "Put",
 | 
				
			||||||
      "QoSOptions": {
 | 
					            "QoSOptions": {
 | 
				
			||||||
        "ExceptionsAllowedBeforeBreaking": 3,
 | 
					                "ExceptionsAllowedBeforeBreaking": 3,
 | 
				
			||||||
        "DurationOfBreak": 10,
 | 
					                "DurationOfBreak": 10,
 | 
				
			||||||
        "TimeoutValue": 5000
 | 
					                "TimeoutValue": 5000
 | 
				
			||||||
      },
 | 
					            },
 | 
				
			||||||
      "FileCacheOptions": { "TtlSeconds": 15 }
 | 
					            "FileCacheOptions": { "TtlSeconds": 15 }
 | 
				
			||||||
    },
 | 
					        },
 | 
				
			||||||
    {
 | 
					        {
 | 
				
			||||||
      "DownstreamPathTemplate": "/api/products/{productId}",
 | 
					            "DownstreamPathTemplate": "/api/products/{productId}",
 | 
				
			||||||
      "DownstreamScheme": "http",
 | 
					            "DownstreamScheme": "http",
 | 
				
			||||||
      "DownstreamHost": "products20161126090340.azurewebsites.net",
 | 
					            "DownstreamHost": "products20161126090340.azurewebsites.net",
 | 
				
			||||||
      "DownstreamPort": 80,
 | 
					            "DownstreamPort": 80,
 | 
				
			||||||
      "UpstreamPathTemplate": "/products/{productId}",
 | 
					            "UpstreamPathTemplate": "/products/{productId}",
 | 
				
			||||||
      "UpstreamHttpMethod": "Delete",
 | 
					            "UpstreamHttpMethod": "Delete",
 | 
				
			||||||
      "QoSOptions": {
 | 
					            "QoSOptions": {
 | 
				
			||||||
        "ExceptionsAllowedBeforeBreaking": 3,
 | 
					                "ExceptionsAllowedBeforeBreaking": 3,
 | 
				
			||||||
        "DurationOfBreak": 10,
 | 
					                "DurationOfBreak": 10,
 | 
				
			||||||
        "TimeoutValue": 5000
 | 
					                "TimeoutValue": 5000
 | 
				
			||||||
      },
 | 
					            },
 | 
				
			||||||
      "FileCacheOptions": { "TtlSeconds": 15 }
 | 
					            "FileCacheOptions": { "TtlSeconds": 15 }
 | 
				
			||||||
    },
 | 
					        },
 | 
				
			||||||
    {
 | 
					        {
 | 
				
			||||||
      "DownstreamPathTemplate": "/api/customers",
 | 
					            "DownstreamPathTemplate": "/api/customers",
 | 
				
			||||||
      "DownstreamScheme": "http",
 | 
					            "DownstreamScheme": "http",
 | 
				
			||||||
      "DownstreamHost": "customers20161126090811.azurewebsites.net",
 | 
					            "DownstreamHost": "customers20161126090811.azurewebsites.net",
 | 
				
			||||||
      "DownstreamPort": 80,
 | 
					            "DownstreamPort": 80,
 | 
				
			||||||
      "UpstreamPathTemplate": "/customers",
 | 
					            "UpstreamPathTemplate": "/customers",
 | 
				
			||||||
      "UpstreamHttpMethod": "Get",
 | 
					            "UpstreamHttpMethod": "Get",
 | 
				
			||||||
      "QoSOptions": {
 | 
					            "QoSOptions": {
 | 
				
			||||||
        "ExceptionsAllowedBeforeBreaking": 3,
 | 
					                "ExceptionsAllowedBeforeBreaking": 3,
 | 
				
			||||||
        "DurationOfBreak": 10,
 | 
					                "DurationOfBreak": 10,
 | 
				
			||||||
        "TimeoutValue": 5000
 | 
					                "TimeoutValue": 5000
 | 
				
			||||||
      },
 | 
					            },
 | 
				
			||||||
      "FileCacheOptions": { "TtlSeconds": 15 }
 | 
					            "FileCacheOptions": { "TtlSeconds": 15 }
 | 
				
			||||||
    },
 | 
					        },
 | 
				
			||||||
    {
 | 
					        {
 | 
				
			||||||
      "DownstreamPathTemplate": "/api/customers/{customerId}",
 | 
					            "DownstreamPathTemplate": "/api/customers/{customerId}",
 | 
				
			||||||
      "DownstreamScheme": "http",
 | 
					            "DownstreamScheme": "http",
 | 
				
			||||||
      "DownstreamHost": "customers20161126090811.azurewebsites.net",
 | 
					            "DownstreamHost": "customers20161126090811.azurewebsites.net",
 | 
				
			||||||
      "DownstreamPort": 80,
 | 
					            "DownstreamPort": 80,
 | 
				
			||||||
      "UpstreamPathTemplate": "/customers/{customerId}",
 | 
					            "UpstreamPathTemplate": "/customers/{customerId}",
 | 
				
			||||||
      "UpstreamHttpMethod": "Get",
 | 
					            "UpstreamHttpMethod": "Get",
 | 
				
			||||||
      "QoSOptions": {
 | 
					            "QoSOptions": {
 | 
				
			||||||
        "ExceptionsAllowedBeforeBreaking": 3,
 | 
					                "ExceptionsAllowedBeforeBreaking": 3,
 | 
				
			||||||
        "DurationOfBreak": 10,
 | 
					                "DurationOfBreak": 10,
 | 
				
			||||||
        "TimeoutValue": 5000
 | 
					                "TimeoutValue": 5000
 | 
				
			||||||
      },
 | 
					            },
 | 
				
			||||||
      "FileCacheOptions": { "TtlSeconds": 15 }
 | 
					            "FileCacheOptions": { "TtlSeconds": 15 }
 | 
				
			||||||
    },
 | 
					        },
 | 
				
			||||||
    {
 | 
					        {
 | 
				
			||||||
      "DownstreamPathTemplate": "/api/customers",
 | 
					            "DownstreamPathTemplate": "/api/customers",
 | 
				
			||||||
      "DownstreamScheme": "http",
 | 
					            "DownstreamScheme": "http",
 | 
				
			||||||
      "DownstreamHost": "customers20161126090811.azurewebsites.net",
 | 
					            "DownstreamHost": "customers20161126090811.azurewebsites.net",
 | 
				
			||||||
      "DownstreamPort": 80,
 | 
					            "DownstreamPort": 80,
 | 
				
			||||||
      "UpstreamPathTemplate": "/customers",
 | 
					            "UpstreamPathTemplate": "/customers",
 | 
				
			||||||
      "UpstreamHttpMethod": "Post",
 | 
					            "UpstreamHttpMethod": "Post",
 | 
				
			||||||
      "QoSOptions": {
 | 
					            "QoSOptions": {
 | 
				
			||||||
        "ExceptionsAllowedBeforeBreaking": 3,
 | 
					                "ExceptionsAllowedBeforeBreaking": 3,
 | 
				
			||||||
        "DurationOfBreak": 10,
 | 
					                "DurationOfBreak": 10,
 | 
				
			||||||
        "TimeoutValue": 5000
 | 
					                "TimeoutValue": 5000
 | 
				
			||||||
      },
 | 
					            },
 | 
				
			||||||
      "FileCacheOptions": { "TtlSeconds": 15 }
 | 
					            "FileCacheOptions": { "TtlSeconds": 15 }
 | 
				
			||||||
    },
 | 
					        },
 | 
				
			||||||
    {
 | 
					        {
 | 
				
			||||||
      "DownstreamPathTemplate": "/api/customers/{customerId}",
 | 
					            "DownstreamPathTemplate": "/api/customers/{customerId}",
 | 
				
			||||||
      "DownstreamScheme": "http",
 | 
					            "DownstreamScheme": "http",
 | 
				
			||||||
      "DownstreamHost": "customers20161126090811.azurewebsites.net",
 | 
					            "DownstreamHost": "customers20161126090811.azurewebsites.net",
 | 
				
			||||||
      "DownstreamPort": 80,
 | 
					            "DownstreamPort": 80,
 | 
				
			||||||
      "UpstreamPathTemplate": "/customers/{customerId}",
 | 
					            "UpstreamPathTemplate": "/customers/{customerId}",
 | 
				
			||||||
      "UpstreamHttpMethod": "Put",
 | 
					            "UpstreamHttpMethod": "Put",
 | 
				
			||||||
      "QoSOptions": {
 | 
					            "QoSOptions": {
 | 
				
			||||||
        "ExceptionsAllowedBeforeBreaking": 3,
 | 
					                "ExceptionsAllowedBeforeBreaking": 3,
 | 
				
			||||||
        "DurationOfBreak": 10,
 | 
					                "DurationOfBreak": 10,
 | 
				
			||||||
        "TimeoutValue": 5000
 | 
					                "TimeoutValue": 5000
 | 
				
			||||||
      },
 | 
					            },
 | 
				
			||||||
      "FileCacheOptions": { "TtlSeconds": 15 }
 | 
					            "FileCacheOptions": { "TtlSeconds": 15 }
 | 
				
			||||||
    },
 | 
					        },
 | 
				
			||||||
    {
 | 
					        {
 | 
				
			||||||
      "DownstreamPathTemplate": "/api/customers/{customerId}",
 | 
					            "DownstreamPathTemplate": "/api/customers/{customerId}",
 | 
				
			||||||
      "DownstreamScheme": "http",
 | 
					            "DownstreamScheme": "http",
 | 
				
			||||||
      "DownstreamHost": "customers20161126090811.azurewebsites.net",
 | 
					            "DownstreamHost": "customers20161126090811.azurewebsites.net",
 | 
				
			||||||
      "DownstreamPort": 80,
 | 
					            "DownstreamPort": 80,
 | 
				
			||||||
      "UpstreamPathTemplate": "/customers/{customerId}",
 | 
					            "UpstreamPathTemplate": "/customers/{customerId}",
 | 
				
			||||||
      "UpstreamHttpMethod": "Delete",
 | 
					            "UpstreamHttpMethod": "Delete",
 | 
				
			||||||
      "QoSOptions": {
 | 
					            "QoSOptions": {
 | 
				
			||||||
        "ExceptionsAllowedBeforeBreaking": 3,
 | 
					                "ExceptionsAllowedBeforeBreaking": 3,
 | 
				
			||||||
        "DurationOfBreak": 10,
 | 
					                "DurationOfBreak": 10,
 | 
				
			||||||
        "TimeoutValue": 5000
 | 
					                "TimeoutValue": 5000
 | 
				
			||||||
      },
 | 
					            },
 | 
				
			||||||
      "FileCacheOptions": { "TtlSeconds": 15 }
 | 
					            "FileCacheOptions": { "TtlSeconds": 15 }
 | 
				
			||||||
    },
 | 
					        },
 | 
				
			||||||
    {
 | 
					        {
 | 
				
			||||||
      "DownstreamPathTemplate": "/posts",
 | 
					            "DownstreamPathTemplate": "/posts",
 | 
				
			||||||
      "DownstreamScheme": "http",
 | 
					            "DownstreamScheme": "http",
 | 
				
			||||||
      "DownstreamHost": "jsonplaceholder.typicode.com",
 | 
					            "DownstreamHost": "jsonplaceholder.typicode.com",
 | 
				
			||||||
      "DownstreamPort": 80,
 | 
					            "DownstreamPort": 80,
 | 
				
			||||||
      "UpstreamPathTemplate": "/posts/",
 | 
					            "UpstreamPathTemplate": "/posts/",
 | 
				
			||||||
      "UpstreamHttpMethod": "Get",
 | 
					            "UpstreamHttpMethod": "Get",
 | 
				
			||||||
      "QoSOptions": {
 | 
					            "QoSOptions": {
 | 
				
			||||||
        "ExceptionsAllowedBeforeBreaking": 3,
 | 
					                "ExceptionsAllowedBeforeBreaking": 3,
 | 
				
			||||||
        "DurationOfBreak": 10,
 | 
					                "DurationOfBreak": 10,
 | 
				
			||||||
        "TimeoutValue": 5000
 | 
					                "TimeoutValue": 5000
 | 
				
			||||||
      },
 | 
					            },
 | 
				
			||||||
      "FileCacheOptions": { "TtlSeconds": 15 }
 | 
					            "FileCacheOptions": { "TtlSeconds": 15 }
 | 
				
			||||||
    }
 | 
					        },
 | 
				
			||||||
  ],
 | 
					        {
 | 
				
			||||||
 | 
					            "DownstreamPathTemplate": "/",
 | 
				
			||||||
 | 
					            "DownstreamScheme": "http",
 | 
				
			||||||
 | 
					            "DownstreamHost": "www.bbc.co.uk",
 | 
				
			||||||
 | 
					            "DownstreamPort": 80,
 | 
				
			||||||
 | 
					            "UpstreamPathTemplate": "/bbc/",
 | 
				
			||||||
 | 
					            "UpstreamHttpMethod": "Get"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  "GlobalConfiguration": {
 | 
					  "GlobalConfiguration": {
 | 
				
			||||||
    "RequestIdKey": "OcRequestId",
 | 
					    "RequestIdKey": "OcRequestId",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,74 @@
 | 
				
			|||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using Ocelot.Configuration;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Builder;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Creator;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					using Shouldly;
 | 
				
			||||||
 | 
					using TestStack.BDDfy;
 | 
				
			||||||
 | 
					using Xunit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.UnitTests.Configuration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class AuthenticationOptionsCreatorTests
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private AuthenticationOptionsCreator _authOptionsCreator;
 | 
				
			||||||
 | 
					        private FileReRoute _fileReRoute;
 | 
				
			||||||
 | 
					        private AuthenticationOptions _result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public AuthenticationOptionsCreatorTests()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _authOptionsCreator = new AuthenticationOptionsCreator();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_return_auth_options()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var fileReRoute = new FileReRoute()
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                AuthenticationOptions = new FileAuthenticationOptions
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    Provider = "Geoff",
 | 
				
			||||||
 | 
					                    ProviderRootUrl = "http://www.bbc.co.uk/",
 | 
				
			||||||
 | 
					                    ScopeName = "Laura",
 | 
				
			||||||
 | 
					                    RequireHttps = true,
 | 
				
			||||||
 | 
					                    AdditionalScopes = new List<string> {"cheese"},
 | 
				
			||||||
 | 
					                    ScopeSecret = "secret"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var expected = 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();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenTheFollowing(fileReRoute))
 | 
				
			||||||
 | 
					                .When(x => x.WhenICreateTheAuthenticationOptions())
 | 
				
			||||||
 | 
					                .Then(x => x.ThenTheFollowingIsReturned(expected))
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenTheFollowing(FileReRoute fileReRoute)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _fileReRoute = fileReRoute;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void WhenICreateTheAuthenticationOptions()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _result = _authOptionsCreator.Create(_fileReRoute);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void ThenTheFollowingIsReturned(AuthenticationOptions expected)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _result.AdditionalScopes.ShouldBe(expected.AdditionalScopes);
 | 
				
			||||||
 | 
					            _result.Provider.ShouldBe(expected.Provider);
 | 
				
			||||||
 | 
					            _result.ProviderRootUrl.ShouldBe(expected.ProviderRootUrl);
 | 
				
			||||||
 | 
					            _result.RequireHttps.ShouldBe(expected.RequireHttps);
 | 
				
			||||||
 | 
					            _result.ScopeName.ShouldBe(expected.ScopeName);
 | 
				
			||||||
 | 
					            _result.ScopeSecret.ShouldBe(expected.ScopeSecret);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										110
									
								
								test/Ocelot.UnitTests/Configuration/ClaimsToThingCreatorTests.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								test/Ocelot.UnitTests/Configuration/ClaimsToThingCreatorTests.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,110 @@
 | 
				
			|||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using Moq;
 | 
				
			||||||
 | 
					using Ocelot.Configuration;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Creator;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Parser;
 | 
				
			||||||
 | 
					using Ocelot.Errors;
 | 
				
			||||||
 | 
					using Ocelot.Logging;
 | 
				
			||||||
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					using Shouldly;
 | 
				
			||||||
 | 
					using TestStack.BDDfy;
 | 
				
			||||||
 | 
					using Xunit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.UnitTests.Configuration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class ClaimsToThingCreatorTests
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private readonly Mock<IClaimToThingConfigurationParser> _configParser;
 | 
				
			||||||
 | 
					        private Dictionary<string,string> _claimsToThings;
 | 
				
			||||||
 | 
					        private ClaimsToThingCreator _claimsToThingsCreator;
 | 
				
			||||||
 | 
					        private Mock<IOcelotLoggerFactory> _loggerFactory;
 | 
				
			||||||
 | 
					        private List<ClaimToThing> _result;
 | 
				
			||||||
 | 
					        private Mock<IOcelotLogger> _logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ClaimsToThingCreatorTests()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _loggerFactory = new Mock<IOcelotLoggerFactory>();
 | 
				
			||||||
 | 
					            _logger = new Mock<IOcelotLogger>();
 | 
				
			||||||
 | 
					            _loggerFactory
 | 
				
			||||||
 | 
					                .Setup(x => x.CreateLogger<ClaimsToThingCreator>())
 | 
				
			||||||
 | 
					                .Returns(_logger.Object);
 | 
				
			||||||
 | 
					            _configParser = new Mock<IClaimToThingConfigurationParser>();
 | 
				
			||||||
 | 
					            _claimsToThingsCreator = new ClaimsToThingCreator(_configParser.Object, _loggerFactory.Object);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_return_claims_to_things()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var userInput = new Dictionary<string,string>()
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                {"CustomerId", "Claims[CustomerId] > value"}
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var claimsToThing = new OkResponse<ClaimToThing>(new ClaimToThing("CustomerId", "CustomerId", "", 0));
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenTheFollowingDictionary(userInput))
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheConfigHeaderExtractorReturns(claimsToThing))
 | 
				
			||||||
 | 
					                .When(x => x.WhenIGetTheThings())
 | 
				
			||||||
 | 
					                .Then(x => x.ThenTheConfigParserIsCalledCorrectly())
 | 
				
			||||||
 | 
					                .And(x => x.ThenClaimsToThingsAreReturned())
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_log_error_if_cannot_parse_claim_to_thing()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					             var userInput = new Dictionary<string,string>()
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                {"CustomerId", "Claims[CustomerId] > value"}
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var claimsToThing = new ErrorResponse<ClaimToThing>(It.IsAny<Error>());
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenTheFollowingDictionary(userInput))
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheConfigHeaderExtractorReturns(claimsToThing))
 | 
				
			||||||
 | 
					                .When(x => x.WhenIGetTheThings())
 | 
				
			||||||
 | 
					                .Then(x => x.ThenTheConfigParserIsCalledCorrectly())
 | 
				
			||||||
 | 
					                .And(x => x.ThenNoClaimsToThingsAreReturned())
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void ThenTheLoggerIsCalledCorrectly()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _logger
 | 
				
			||||||
 | 
					                .Verify(x => x.LogDebug(It.IsAny<string>(), It.IsAny<string>()), Times.Once);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void ThenClaimsToThingsAreReturned()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _result.Count.ShouldBeGreaterThan(0);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        private void GivenTheFollowingDictionary(Dictionary<string,string> claimsToThings)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _claimsToThings = claimsToThings;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenTheConfigHeaderExtractorReturns(Response<ClaimToThing> expected)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _configParser
 | 
				
			||||||
 | 
					                .Setup(x => x.Extract(It.IsAny<string>(), It.IsAny<string>()))
 | 
				
			||||||
 | 
					                .Returns(expected);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void ThenNoClaimsToThingsAreReturned()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _result.Count.ShouldBe(0);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void WhenIGetTheThings()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _result = _claimsToThingsCreator.Create(_claimsToThings);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void ThenTheConfigParserIsCalledCorrectly()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _configParser
 | 
				
			||||||
 | 
					                .Verify(x => x.Extract(_claimsToThings.First().Key, _claimsToThings.First().Value), Times.Once);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -6,7 +6,6 @@ using Ocelot.Configuration;
 | 
				
			|||||||
using Ocelot.Configuration.Builder;
 | 
					using Ocelot.Configuration.Builder;
 | 
				
			||||||
using Ocelot.Configuration.Creator;
 | 
					using Ocelot.Configuration.Creator;
 | 
				
			||||||
using Ocelot.Configuration.File;
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
using Ocelot.Configuration.Parser;
 | 
					 | 
				
			||||||
using Ocelot.Configuration.Validator;
 | 
					using Ocelot.Configuration.Validator;
 | 
				
			||||||
using Ocelot.LoadBalancer.LoadBalancers;
 | 
					using Ocelot.LoadBalancer.LoadBalancers;
 | 
				
			||||||
using Ocelot.Requester.QoS;
 | 
					using Ocelot.Requester.QoS;
 | 
				
			||||||
@@ -23,7 +22,6 @@ namespace Ocelot.UnitTests.Configuration
 | 
				
			|||||||
        private readonly Mock<IConfigurationValidator> _validator;
 | 
					        private readonly Mock<IConfigurationValidator> _validator;
 | 
				
			||||||
        private Response<IOcelotConfiguration> _config;
 | 
					        private Response<IOcelotConfiguration> _config;
 | 
				
			||||||
        private FileConfiguration _fileConfiguration;
 | 
					        private FileConfiguration _fileConfiguration;
 | 
				
			||||||
        private readonly Mock<IClaimToThingConfigurationParser> _configParser;
 | 
					 | 
				
			||||||
        private readonly Mock<ILogger<FileOcelotConfigurationCreator>> _logger;
 | 
					        private readonly Mock<ILogger<FileOcelotConfigurationCreator>> _logger;
 | 
				
			||||||
        private readonly FileOcelotConfigurationCreator _ocelotConfigurationCreator;
 | 
					        private readonly FileOcelotConfigurationCreator _ocelotConfigurationCreator;
 | 
				
			||||||
        private readonly Mock<ILoadBalancerFactory> _loadBalancerFactory;
 | 
					        private readonly Mock<ILoadBalancerFactory> _loadBalancerFactory;
 | 
				
			||||||
@@ -32,6 +30,14 @@ namespace Ocelot.UnitTests.Configuration
 | 
				
			|||||||
        private readonly Mock<IQoSProviderFactory> _qosProviderFactory;
 | 
					        private readonly Mock<IQoSProviderFactory> _qosProviderFactory;
 | 
				
			||||||
        private readonly Mock<IQosProviderHouse> _qosProviderHouse;
 | 
					        private readonly Mock<IQosProviderHouse> _qosProviderHouse;
 | 
				
			||||||
        private readonly Mock<IQoSProvider> _qosProvider;
 | 
					        private readonly Mock<IQoSProvider> _qosProvider;
 | 
				
			||||||
 | 
					        private Mock<IClaimsToThingCreator> _claimsToThingCreator;
 | 
				
			||||||
 | 
					        private Mock<IAuthenticationOptionsCreator> _authOptionsCreator;
 | 
				
			||||||
 | 
					        private Mock<IUpstreamTemplatePatternCreator> _upstreamTemplatePatternCreator;
 | 
				
			||||||
 | 
					        private Mock<IRequestIdKeyCreator> _requestIdKeyCreator;
 | 
				
			||||||
 | 
					        private Mock<IServiceProviderConfigurationCreator> _serviceProviderConfigCreator;
 | 
				
			||||||
 | 
					        private Mock<IQoSOptionsCreator> _qosOptionsCreator;
 | 
				
			||||||
 | 
					        private Mock<IReRouteOptionsCreator> _fileReRouteOptionsCreator;
 | 
				
			||||||
 | 
					        private Mock<IRateLimitOptionsCreator> _rateLimitOptions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public FileConfigurationCreatorTests()
 | 
					        public FileConfigurationCreatorTests()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -39,46 +45,69 @@ namespace Ocelot.UnitTests.Configuration
 | 
				
			|||||||
            _qosProviderHouse = new Mock<IQosProviderHouse>();
 | 
					            _qosProviderHouse = new Mock<IQosProviderHouse>();
 | 
				
			||||||
            _qosProvider = new Mock<IQoSProvider>();
 | 
					            _qosProvider = new Mock<IQoSProvider>();
 | 
				
			||||||
            _logger = new Mock<ILogger<FileOcelotConfigurationCreator>>();
 | 
					            _logger = new Mock<ILogger<FileOcelotConfigurationCreator>>();
 | 
				
			||||||
            _configParser = new Mock<IClaimToThingConfigurationParser>();
 | 
					 | 
				
			||||||
            _validator = new Mock<IConfigurationValidator>();
 | 
					            _validator = new Mock<IConfigurationValidator>();
 | 
				
			||||||
            _fileConfig = new Mock<IOptions<FileConfiguration>>();
 | 
					            _fileConfig = new Mock<IOptions<FileConfiguration>>();
 | 
				
			||||||
            _loadBalancerFactory = new Mock<ILoadBalancerFactory>();
 | 
					            _loadBalancerFactory = new Mock<ILoadBalancerFactory>();
 | 
				
			||||||
            _loadBalancerHouse = new Mock<ILoadBalancerHouse>();
 | 
					            _loadBalancerHouse = new Mock<ILoadBalancerHouse>();
 | 
				
			||||||
            _loadBalancer = new Mock<ILoadBalancer>();
 | 
					            _loadBalancer = new Mock<ILoadBalancer>();
 | 
				
			||||||
 | 
					            _claimsToThingCreator = new Mock<IClaimsToThingCreator>();
 | 
				
			||||||
 | 
					            _authOptionsCreator = new Mock<IAuthenticationOptionsCreator>();
 | 
				
			||||||
 | 
					            _upstreamTemplatePatternCreator = new Mock<IUpstreamTemplatePatternCreator>();
 | 
				
			||||||
 | 
					            _requestIdKeyCreator = new Mock<IRequestIdKeyCreator>();
 | 
				
			||||||
 | 
					            _serviceProviderConfigCreator = new Mock<IServiceProviderConfigurationCreator>();
 | 
				
			||||||
 | 
					            _qosOptionsCreator = new Mock<IQoSOptionsCreator>();
 | 
				
			||||||
 | 
					            _fileReRouteOptionsCreator = new Mock<IReRouteOptionsCreator>();
 | 
				
			||||||
 | 
					            _rateLimitOptions = new Mock<IRateLimitOptionsCreator>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _ocelotConfigurationCreator = new FileOcelotConfigurationCreator( 
 | 
					            _ocelotConfigurationCreator = new FileOcelotConfigurationCreator( 
 | 
				
			||||||
                _fileConfig.Object, _validator.Object, _configParser.Object, _logger.Object,
 | 
					                _fileConfig.Object, _validator.Object, _logger.Object,
 | 
				
			||||||
                _loadBalancerFactory.Object, _loadBalancerHouse.Object, 
 | 
					                _loadBalancerFactory.Object, _loadBalancerHouse.Object, 
 | 
				
			||||||
                _qosProviderFactory.Object, _qosProviderHouse.Object);
 | 
					                _qosProviderFactory.Object, _qosProviderHouse.Object, _claimsToThingCreator.Object,
 | 
				
			||||||
 | 
					                _authOptionsCreator.Object, _upstreamTemplatePatternCreator.Object, _requestIdKeyCreator.Object,
 | 
				
			||||||
 | 
					                _serviceProviderConfigCreator.Object, _qosOptionsCreator.Object, _fileReRouteOptionsCreator.Object,
 | 
				
			||||||
 | 
					                _rateLimitOptions.Object);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
        public void should_create_load_balancer()
 | 
					        public void should_call_rate_limit_options_creator()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            var reRouteOptions = new ReRouteOptionsBuilder()
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.Given(x => x.GivenTheConfigIs(new FileConfiguration
 | 
					            this.Given(x => x.GivenTheConfigIs(new FileConfiguration
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ReRoutes = new List<FileReRoute>
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                ReRoutes = new List<FileReRoute>
 | 
					                                new FileReRoute
 | 
				
			||||||
                                {
 | 
					                                {
 | 
				
			||||||
                                    new FileReRoute
 | 
					                                    DownstreamHost = "127.0.0.1",
 | 
				
			||||||
                                    {
 | 
					                                    UpstreamPathTemplate = "/api/products/{productId}",
 | 
				
			||||||
                                        DownstreamHost = "127.0.0.1",
 | 
					                                    DownstreamPathTemplate = "/products/{productId}",
 | 
				
			||||||
                                        UpstreamPathTemplate = "/api/products/{productId}",
 | 
					                                    UpstreamHttpMethod = "Get",
 | 
				
			||||||
                                        DownstreamPathTemplate = "/products/{productId}",
 | 
					                                }
 | 
				
			||||||
                                        UpstreamHttpMethod = "Get",
 | 
					                            },
 | 
				
			||||||
                                    }
 | 
					            }))
 | 
				
			||||||
                                },
 | 
					                .And(x => x.GivenTheConfigIsValid())
 | 
				
			||||||
                            }))
 | 
					                .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
 | 
				
			||||||
                                .And(x => x.GivenTheConfigIsValid())
 | 
					                .When(x => x.WhenICreateTheConfig())
 | 
				
			||||||
                                .And(x => x.GivenTheLoadBalancerFactoryReturns())
 | 
					                .Then(x => x.ThenTheRateLimitOptionsCreatorIsCalledCorrectly())
 | 
				
			||||||
                                .When(x => x.WhenICreateTheConfig())
 | 
					                .BDDfy();
 | 
				
			||||||
                                .Then(x => x.TheLoadBalancerFactoryIsCalledCorrectly())
 | 
					 | 
				
			||||||
                                .And(x => x.ThenTheLoadBalancerHouseIsCalledCorrectly())
 | 
					 | 
				
			||||||
                    .BDDfy();
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
        public void should_create_qos_provider()
 | 
					        public void should_call_qos_options_creator()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            this.Given(x => x.GivenTheConfigIs(new FileConfiguration
 | 
					            var expected = new QoSOptionsBuilder()
 | 
				
			||||||
 | 
					                .WithDurationOfBreak(1)
 | 
				
			||||||
 | 
					                .WithExceptionsAllowedBeforeBreaking(1)
 | 
				
			||||||
 | 
					                .WithTimeoutValue(1)
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var serviceOptions = new ReRouteOptionsBuilder()
 | 
				
			||||||
 | 
					                .WithIsQos(true)
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					             this.Given(x => x.GivenTheConfigIs(new FileConfiguration
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                ReRoutes = new List<FileReRoute>
 | 
					                ReRoutes = new List<FileReRoute>
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@@ -98,17 +127,23 @@ namespace Ocelot.UnitTests.Configuration
 | 
				
			|||||||
                },
 | 
					                },
 | 
				
			||||||
            }))
 | 
					            }))
 | 
				
			||||||
                .And(x => x.GivenTheConfigIsValid())
 | 
					                .And(x => x.GivenTheConfigIsValid())
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheFollowingOptionsAreReturned(serviceOptions))
 | 
				
			||||||
                .And(x => x.GivenTheQosProviderFactoryReturns())
 | 
					                .And(x => x.GivenTheQosProviderFactoryReturns())
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheQosOptionsCreatorReturns(expected))
 | 
				
			||||||
                .When(x => x.WhenICreateTheConfig())
 | 
					                .When(x => x.WhenICreateTheConfig())
 | 
				
			||||||
                .Then(x => x.TheQosProviderFactoryIsCalledCorrectly())
 | 
					                .Then(x => x.ThenTheQosOptionsAre(expected))
 | 
				
			||||||
 | 
					                .And(x => x.TheQosProviderFactoryIsCalledCorrectly())
 | 
				
			||||||
                .And(x => x.ThenTheQosProviderHouseIsCalledCorrectly())
 | 
					                .And(x => x.ThenTheQosProviderHouseIsCalledCorrectly())
 | 
				
			||||||
                .BDDfy();
 | 
					                .BDDfy();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
        public void should_use_downstream_host()
 | 
					        public void should_create_load_balancer()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
                this.Given(x => x.GivenTheConfigIs(new FileConfiguration
 | 
					            var reRouteOptions = new ReRouteOptionsBuilder()
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenTheConfigIs(new FileConfiguration
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                ReRoutes = new List<FileReRoute>
 | 
					                                ReRoutes = new List<FileReRoute>
 | 
				
			||||||
                                {
 | 
					                                {
 | 
				
			||||||
@@ -122,23 +157,54 @@ namespace Ocelot.UnitTests.Configuration
 | 
				
			|||||||
                                },
 | 
					                                },
 | 
				
			||||||
                            }))
 | 
					                            }))
 | 
				
			||||||
                                .And(x => x.GivenTheConfigIsValid())
 | 
					                                .And(x => x.GivenTheConfigIsValid())
 | 
				
			||||||
 | 
					                                .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
 | 
				
			||||||
 | 
					                                .And(x => x.GivenTheLoadBalancerFactoryReturns())
 | 
				
			||||||
                                .When(x => x.WhenICreateTheConfig())
 | 
					                                .When(x => x.WhenICreateTheConfig())
 | 
				
			||||||
                                .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
 | 
					                                .Then(x => x.TheLoadBalancerFactoryIsCalledCorrectly())
 | 
				
			||||||
                                {
 | 
					                                .And(x => x.ThenTheLoadBalancerHouseIsCalledCorrectly())
 | 
				
			||||||
                                    new ReRouteBuilder()
 | 
					 | 
				
			||||||
                                        .WithDownstreamHost("127.0.0.1")
 | 
					 | 
				
			||||||
                                        .WithDownstreamPathTemplate("/products/{productId}")
 | 
					 | 
				
			||||||
                                        .WithUpstreamPathTemplate("/api/products/{productId}")
 | 
					 | 
				
			||||||
                                        .WithUpstreamHttpMethod("Get")
 | 
					 | 
				
			||||||
                                        .WithUpstreamTemplatePattern("(?i)/api/products/.*/$")
 | 
					 | 
				
			||||||
                                        .Build()
 | 
					 | 
				
			||||||
                                }))
 | 
					 | 
				
			||||||
                    .BDDfy();
 | 
					                    .BDDfy();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_use_downstream_host()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var reRouteOptions = new ReRouteOptionsBuilder()
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenTheConfigIs(new FileConfiguration
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            ReRoutes = new List<FileReRoute>
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                new FileReRoute
 | 
				
			||||||
 | 
					                                {
 | 
				
			||||||
 | 
					                                    DownstreamHost = "127.0.0.1",
 | 
				
			||||||
 | 
					                                    UpstreamPathTemplate = "/api/products/{productId}",
 | 
				
			||||||
 | 
					                                    DownstreamPathTemplate = "/products/{productId}",
 | 
				
			||||||
 | 
					                                    UpstreamHttpMethod = "Get",
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            },
 | 
				
			||||||
 | 
					                        }))
 | 
				
			||||||
 | 
					                            .And(x => x.GivenTheConfigIsValid())
 | 
				
			||||||
 | 
					                            .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
 | 
				
			||||||
 | 
					                            .When(x => x.WhenICreateTheConfig())
 | 
				
			||||||
 | 
					                            .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                new ReRouteBuilder()
 | 
				
			||||||
 | 
					                                    .WithDownstreamHost("127.0.0.1")
 | 
				
			||||||
 | 
					                                    .WithDownstreamPathTemplate("/products/{productId}")
 | 
				
			||||||
 | 
					                                    .WithUpstreamPathTemplate("/api/products/{productId}")
 | 
				
			||||||
 | 
					                                    .WithUpstreamHttpMethod("Get")
 | 
				
			||||||
 | 
					                                    .Build()
 | 
				
			||||||
 | 
					                            }))
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
        public void should_use_downstream_scheme()
 | 
					        public void should_use_downstream_scheme()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            var reRouteOptions = new ReRouteOptionsBuilder()
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.Given(x => x.GivenTheConfigIs(new FileConfiguration
 | 
					            this.Given(x => x.GivenTheConfigIs(new FileConfiguration
 | 
				
			||||||
                                        {
 | 
					                                        {
 | 
				
			||||||
                                            ReRoutes = new List<FileReRoute>
 | 
					                                            ReRoutes = new List<FileReRoute>
 | 
				
			||||||
@@ -153,6 +219,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
				
			|||||||
                                            },
 | 
					                                            },
 | 
				
			||||||
                                        }))
 | 
					                                        }))
 | 
				
			||||||
                                            .And(x => x.GivenTheConfigIsValid())
 | 
					                                            .And(x => x.GivenTheConfigIsValid())
 | 
				
			||||||
 | 
					                                            .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
 | 
				
			||||||
                                            .When(x => x.WhenICreateTheConfig())
 | 
					                                            .When(x => x.WhenICreateTheConfig())
 | 
				
			||||||
                                            .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
 | 
					                                            .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
 | 
				
			||||||
                                            {
 | 
					                                            {
 | 
				
			||||||
@@ -161,7 +228,6 @@ namespace Ocelot.UnitTests.Configuration
 | 
				
			|||||||
                                                    .WithDownstreamPathTemplate("/products/{productId}")
 | 
					                                                    .WithDownstreamPathTemplate("/products/{productId}")
 | 
				
			||||||
                                                    .WithUpstreamPathTemplate("/api/products/{productId}")
 | 
					                                                    .WithUpstreamPathTemplate("/api/products/{productId}")
 | 
				
			||||||
                                                    .WithUpstreamHttpMethod("Get")
 | 
					                                                    .WithUpstreamHttpMethod("Get")
 | 
				
			||||||
                                                    .WithUpstreamTemplatePattern("(?i)/api/products/.*/$")
 | 
					 | 
				
			||||||
                                                    .Build()
 | 
					                                                    .Build()
 | 
				
			||||||
                                            }))
 | 
					                                            }))
 | 
				
			||||||
                                .BDDfy();
 | 
					                                .BDDfy();
 | 
				
			||||||
@@ -170,6 +236,9 @@ namespace Ocelot.UnitTests.Configuration
 | 
				
			|||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
        public void should_use_service_discovery_for_downstream_service_host()
 | 
					        public void should_use_service_discovery_for_downstream_service_host()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            var reRouteOptions = new ReRouteOptionsBuilder()
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.Given(x => x.GivenTheConfigIs(new FileConfiguration
 | 
					            this.Given(x => x.GivenTheConfigIs(new FileConfiguration
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            ReRoutes = new List<FileReRoute>
 | 
					                            ReRoutes = new List<FileReRoute>
 | 
				
			||||||
@@ -193,6 +262,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
				
			|||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }))
 | 
					                        }))
 | 
				
			||||||
                            .And(x => x.GivenTheConfigIsValid())
 | 
					                            .And(x => x.GivenTheConfigIsValid())
 | 
				
			||||||
 | 
					                            .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
 | 
				
			||||||
                            .When(x => x.WhenICreateTheConfig())
 | 
					                            .When(x => x.WhenICreateTheConfig())
 | 
				
			||||||
                            .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
 | 
					                            .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
@@ -200,8 +270,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
				
			|||||||
                                    .WithDownstreamPathTemplate("/products/{productId}")
 | 
					                                    .WithDownstreamPathTemplate("/products/{productId}")
 | 
				
			||||||
                                    .WithUpstreamPathTemplate("/api/products/{productId}")
 | 
					                                    .WithUpstreamPathTemplate("/api/products/{productId}")
 | 
				
			||||||
                                    .WithUpstreamHttpMethod("Get")
 | 
					                                    .WithUpstreamHttpMethod("Get")
 | 
				
			||||||
                                    .WithUpstreamTemplatePattern("(?i)/api/products/.*/$")
 | 
					                                    .WithServiceProviderConfiguraion(new ServiceProviderConfigurationBuilder()
 | 
				
			||||||
                                    .WithServiceProviderConfiguraion(new ServiceProviderConfiguraionBuilder()
 | 
					 | 
				
			||||||
                                        .WithUseServiceDiscovery(true)
 | 
					                                        .WithUseServiceDiscovery(true)
 | 
				
			||||||
                                        .WithServiceDiscoveryProvider("consul")
 | 
					                                        .WithServiceDiscoveryProvider("consul")
 | 
				
			||||||
                                        .WithServiceDiscoveryProviderHost("127.0.0.1")
 | 
					                                        .WithServiceDiscoveryProviderHost("127.0.0.1")
 | 
				
			||||||
@@ -215,6 +284,9 @@ namespace Ocelot.UnitTests.Configuration
 | 
				
			|||||||
         [Fact]
 | 
					         [Fact]
 | 
				
			||||||
        public void should_not_use_service_discovery_for_downstream_host_url_when_no_service_name()
 | 
					        public void should_not_use_service_discovery_for_downstream_host_url_when_no_service_name()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            var reRouteOptions = new ReRouteOptionsBuilder()
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
            this.Given(x => x.GivenTheConfigIs(new FileConfiguration
 | 
					            this.Given(x => x.GivenTheConfigIs(new FileConfiguration
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            ReRoutes = new List<FileReRoute>
 | 
					                            ReRoutes = new List<FileReRoute>
 | 
				
			||||||
@@ -229,6 +301,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
				
			|||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }))
 | 
					                        }))
 | 
				
			||||||
                            .And(x => x.GivenTheConfigIsValid())
 | 
					                            .And(x => x.GivenTheConfigIsValid())
 | 
				
			||||||
 | 
					                            .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
 | 
				
			||||||
                            .When(x => x.WhenICreateTheConfig())
 | 
					                            .When(x => x.WhenICreateTheConfig())
 | 
				
			||||||
                            .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
 | 
					                            .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
@@ -236,8 +309,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
				
			|||||||
                                    .WithDownstreamPathTemplate("/products/{productId}")
 | 
					                                    .WithDownstreamPathTemplate("/products/{productId}")
 | 
				
			||||||
                                    .WithUpstreamPathTemplate("/api/products/{productId}")
 | 
					                                    .WithUpstreamPathTemplate("/api/products/{productId}")
 | 
				
			||||||
                                    .WithUpstreamHttpMethod("Get")
 | 
					                                    .WithUpstreamHttpMethod("Get")
 | 
				
			||||||
                                    .WithUpstreamTemplatePattern("(?i)/api/products/.*/$")
 | 
					                                    .WithServiceProviderConfiguraion(new ServiceProviderConfigurationBuilder()
 | 
				
			||||||
                                    .WithServiceProviderConfiguraion(new ServiceProviderConfiguraionBuilder()
 | 
					 | 
				
			||||||
                                        .WithUseServiceDiscovery(false)
 | 
					                                        .WithUseServiceDiscovery(false)
 | 
				
			||||||
                                        .Build())
 | 
					                                        .Build())
 | 
				
			||||||
                                    .Build()
 | 
					                                    .Build()
 | 
				
			||||||
@@ -246,8 +318,11 @@ namespace Ocelot.UnitTests.Configuration
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
        public void should_use_reroute_case_sensitivity_value()
 | 
					        public void should_call_template_pattern_creator_correctly()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					             var reRouteOptions = new ReRouteOptionsBuilder()
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
            this.Given(x => x.GivenTheConfigIs(new FileConfiguration
 | 
					            this.Given(x => x.GivenTheConfigIs(new FileConfiguration
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                ReRoutes = new List<FileReRoute>
 | 
					                ReRoutes = new List<FileReRoute>
 | 
				
			||||||
@@ -262,6 +337,8 @@ namespace Ocelot.UnitTests.Configuration
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }))
 | 
					            }))
 | 
				
			||||||
                .And(x => x.GivenTheConfigIsValid())
 | 
					                .And(x => x.GivenTheConfigIsValid())
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheUpstreamTemplatePatternCreatorReturns("(?i)/api/products/.*/$"))
 | 
				
			||||||
                .When(x => x.WhenICreateTheConfig())
 | 
					                .When(x => x.WhenICreateTheConfig())
 | 
				
			||||||
                .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
 | 
					                .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@@ -276,67 +353,11 @@ namespace Ocelot.UnitTests.Configuration
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
        public void should_set_upstream_template_pattern_to_ignore_case_sensitivity()
 | 
					        public void should_call_request_id_creator()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            this.Given(x => x.GivenTheConfigIs(new FileConfiguration
 | 
					            var reRouteOptions = new ReRouteOptionsBuilder()
 | 
				
			||||||
            {
 | 
					                .Build();
 | 
				
			||||||
                ReRoutes = new List<FileReRoute>
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    new FileReRoute
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        UpstreamPathTemplate = "/api/products/{productId}",
 | 
					 | 
				
			||||||
                        DownstreamPathTemplate = "/products/{productId}",
 | 
					 | 
				
			||||||
                        UpstreamHttpMethod = "Get"
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }))
 | 
					 | 
				
			||||||
                .And(x => x.GivenTheConfigIsValid())
 | 
					 | 
				
			||||||
                .When(x => x.WhenICreateTheConfig())
 | 
					 | 
				
			||||||
                .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    new ReRouteBuilder()
 | 
					 | 
				
			||||||
                        .WithDownstreamPathTemplate("/products/{productId}")
 | 
					 | 
				
			||||||
                        .WithUpstreamPathTemplate("/api/products/{productId}")
 | 
					 | 
				
			||||||
                        .WithUpstreamHttpMethod("Get")
 | 
					 | 
				
			||||||
                        .WithUpstreamTemplatePattern("(?i)/api/products/.*/$")
 | 
					 | 
				
			||||||
                        .Build()
 | 
					 | 
				
			||||||
                }))
 | 
					 | 
				
			||||||
                .BDDfy();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Fact]
 | 
					 | 
				
			||||||
        public void should_set_upstream_template_pattern_to_respect_case_sensitivity()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            this.Given(x => x.GivenTheConfigIs(new FileConfiguration
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                ReRoutes = new List<FileReRoute>
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    new FileReRoute
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        UpstreamPathTemplate = "/api/products/{productId}",
 | 
					 | 
				
			||||||
                        DownstreamPathTemplate = "/products/{productId}",
 | 
					 | 
				
			||||||
                        UpstreamHttpMethod = "Get",
 | 
					 | 
				
			||||||
                        ReRouteIsCaseSensitive = true
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }))
 | 
					 | 
				
			||||||
              .And(x => x.GivenTheConfigIsValid())
 | 
					 | 
				
			||||||
              .When(x => x.WhenICreateTheConfig())
 | 
					 | 
				
			||||||
              .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
 | 
					 | 
				
			||||||
              {
 | 
					 | 
				
			||||||
                    new ReRouteBuilder()
 | 
					 | 
				
			||||||
                        .WithDownstreamPathTemplate("/products/{productId}")
 | 
					 | 
				
			||||||
                        .WithUpstreamPathTemplate("/api/products/{productId}")
 | 
					 | 
				
			||||||
                        .WithUpstreamHttpMethod("Get")
 | 
					 | 
				
			||||||
                        .WithUpstreamTemplatePattern("/api/products/.*/$")
 | 
					 | 
				
			||||||
                        .Build()
 | 
					 | 
				
			||||||
              }))
 | 
					 | 
				
			||||||
              .BDDfy();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [Fact]
 | 
					 | 
				
			||||||
        public void should_set_global_request_id_key()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            this.Given(x => x.GivenTheConfigIs(new FileConfiguration
 | 
					            this.Given(x => x.GivenTheConfigIs(new FileConfiguration
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                ReRoutes = new List<FileReRoute>
 | 
					                ReRoutes = new List<FileReRoute>
 | 
				
			||||||
@@ -355,6 +376,8 @@ namespace Ocelot.UnitTests.Configuration
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }))
 | 
					            }))
 | 
				
			||||||
                .And(x => x.GivenTheConfigIsValid())    
 | 
					                .And(x => x.GivenTheConfigIsValid())    
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheRequestIdCreatorReturns("blahhhh"))
 | 
				
			||||||
                .When(x => x.WhenICreateTheConfig())
 | 
					                .When(x => x.WhenICreateTheConfig())
 | 
				
			||||||
                .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
 | 
					                .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@@ -362,46 +385,20 @@ namespace Ocelot.UnitTests.Configuration
 | 
				
			|||||||
                        .WithDownstreamPathTemplate("/products/{productId}")
 | 
					                        .WithDownstreamPathTemplate("/products/{productId}")
 | 
				
			||||||
                        .WithUpstreamPathTemplate("/api/products/{productId}")
 | 
					                        .WithUpstreamPathTemplate("/api/products/{productId}")
 | 
				
			||||||
                        .WithUpstreamHttpMethod("Get")
 | 
					                        .WithUpstreamHttpMethod("Get")
 | 
				
			||||||
                        .WithUpstreamTemplatePattern("/api/products/.*/$")
 | 
					 | 
				
			||||||
                        .WithRequestIdKey("blahhhh")
 | 
					                        .WithRequestIdKey("blahhhh")
 | 
				
			||||||
                        .Build()
 | 
					                        .Build()
 | 
				
			||||||
                }))
 | 
					                }))
 | 
				
			||||||
                .BDDfy();
 | 
					                .And(x => x.ThenTheRequestIdKeyCreatorIsCalledCorrectly())
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [Fact]
 | 
					 | 
				
			||||||
        public void should_create_template_pattern_that_matches_anything_to_end_of_string()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            this.Given(x => x.GivenTheConfigIs(new FileConfiguration
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                ReRoutes = new List<FileReRoute>
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    new FileReRoute
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        UpstreamPathTemplate = "/api/products/{productId}",
 | 
					 | 
				
			||||||
                        DownstreamPathTemplate = "/products/{productId}",
 | 
					 | 
				
			||||||
                        UpstreamHttpMethod = "Get",
 | 
					 | 
				
			||||||
                        ReRouteIsCaseSensitive = true
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }))
 | 
					 | 
				
			||||||
                .And(x => x.GivenTheConfigIsValid())
 | 
					 | 
				
			||||||
                .When(x => x.WhenICreateTheConfig())
 | 
					 | 
				
			||||||
                .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    new ReRouteBuilder()
 | 
					 | 
				
			||||||
                        .WithDownstreamPathTemplate("/products/{productId}")
 | 
					 | 
				
			||||||
                        .WithUpstreamPathTemplate("/api/products/{productId}")
 | 
					 | 
				
			||||||
                        .WithUpstreamHttpMethod("Get")
 | 
					 | 
				
			||||||
                        .WithUpstreamTemplatePattern("/api/products/.*/$")
 | 
					 | 
				
			||||||
                        .Build()
 | 
					 | 
				
			||||||
                }))
 | 
					 | 
				
			||||||
                .BDDfy();
 | 
					                .BDDfy();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
        public void should_create_with_headers_to_extract()
 | 
					        public void should_create_with_headers_to_extract()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            var reRouteOptions = new ReRouteOptionsBuilder()
 | 
				
			||||||
 | 
					                .WithIsAuthenticated(true)
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var authenticationOptions = new AuthenticationOptionsBuilder()
 | 
					            var authenticationOptions = new AuthenticationOptionsBuilder()
 | 
				
			||||||
                    .WithProvider("IdentityServer")
 | 
					                    .WithProvider("IdentityServer")
 | 
				
			||||||
                    .WithProviderRootUrl("http://localhost:51888")
 | 
					                    .WithProviderRootUrl("http://localhost:51888")
 | 
				
			||||||
@@ -417,7 +414,6 @@ namespace Ocelot.UnitTests.Configuration
 | 
				
			|||||||
                    .WithDownstreamPathTemplate("/products/{productId}")
 | 
					                    .WithDownstreamPathTemplate("/products/{productId}")
 | 
				
			||||||
                    .WithUpstreamPathTemplate("/api/products/{productId}")
 | 
					                    .WithUpstreamPathTemplate("/api/products/{productId}")
 | 
				
			||||||
                    .WithUpstreamHttpMethod("Get")
 | 
					                    .WithUpstreamHttpMethod("Get")
 | 
				
			||||||
                    .WithUpstreamTemplatePattern("/api/products/.*/$")
 | 
					 | 
				
			||||||
                    .WithAuthenticationOptions(authenticationOptions)
 | 
					                    .WithAuthenticationOptions(authenticationOptions)
 | 
				
			||||||
                    .WithClaimsToHeaders(new List<ClaimToThing>
 | 
					                    .WithClaimsToHeaders(new List<ClaimToThing>
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
@@ -453,24 +449,24 @@ namespace Ocelot.UnitTests.Configuration
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }))
 | 
					            }))
 | 
				
			||||||
                .And(x => x.GivenTheConfigIsValid())
 | 
					                .And(x => x.GivenTheConfigIsValid())
 | 
				
			||||||
                .And(x => x.GivenTheConfigHeaderExtractorReturns(new ClaimToThing("CustomerId", "CustomerId", "", 0)))
 | 
					                .And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions))
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheClaimsToThingCreatorReturns(new List<ClaimToThing>{new ClaimToThing("CustomerId", "CustomerId", "", 0)}))
 | 
				
			||||||
                .And(x => x.GivenTheLoadBalancerFactoryReturns())
 | 
					                .And(x => x.GivenTheLoadBalancerFactoryReturns())
 | 
				
			||||||
                .When(x => x.WhenICreateTheConfig())
 | 
					                .When(x => x.WhenICreateTheConfig())
 | 
				
			||||||
                .Then(x => x.ThenTheReRoutesAre(expected))
 | 
					                .Then(x => x.ThenTheReRoutesAre(expected))
 | 
				
			||||||
                .And(x => x.ThenTheAuthenticationOptionsAre(expected))
 | 
					                .And(x => x.ThenTheAuthenticationOptionsAre(expected))
 | 
				
			||||||
 | 
					                .And(x => x.ThenTheAuthOptionsCreatorIsCalledCorrectly())
 | 
				
			||||||
                .BDDfy();
 | 
					                .BDDfy();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void GivenTheConfigHeaderExtractorReturns(ClaimToThing expected)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _configParser
 | 
					 | 
				
			||||||
                .Setup(x => x.Extract(It.IsAny<string>(), It.IsAny<string>()))
 | 
					 | 
				
			||||||
                .Returns(new OkResponse<ClaimToThing>(expected));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
        public void should_create_with_authentication_properties()
 | 
					        public void should_create_with_authentication_properties()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            var reRouteOptions = new ReRouteOptionsBuilder()
 | 
				
			||||||
 | 
					                .WithIsAuthenticated(true)
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
             var authenticationOptions = new AuthenticationOptionsBuilder()
 | 
					             var authenticationOptions = new AuthenticationOptionsBuilder()
 | 
				
			||||||
                    .WithProvider("IdentityServer")
 | 
					                    .WithProvider("IdentityServer")
 | 
				
			||||||
                    .WithProviderRootUrl("http://localhost:51888")
 | 
					                    .WithProviderRootUrl("http://localhost:51888")
 | 
				
			||||||
@@ -486,7 +482,6 @@ namespace Ocelot.UnitTests.Configuration
 | 
				
			|||||||
                    .WithDownstreamPathTemplate("/products/{productId}")
 | 
					                    .WithDownstreamPathTemplate("/products/{productId}")
 | 
				
			||||||
                    .WithUpstreamPathTemplate("/api/products/{productId}")
 | 
					                    .WithUpstreamPathTemplate("/api/products/{productId}")
 | 
				
			||||||
                    .WithUpstreamHttpMethod("Get")
 | 
					                    .WithUpstreamHttpMethod("Get")
 | 
				
			||||||
                    .WithUpstreamTemplatePattern("/api/products/.*/$")
 | 
					 | 
				
			||||||
                    .WithAuthenticationOptions(authenticationOptions)
 | 
					                    .WithAuthenticationOptions(authenticationOptions)
 | 
				
			||||||
                    .Build()
 | 
					                    .Build()
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
@@ -514,101 +509,27 @@ namespace Ocelot.UnitTests.Configuration
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }))
 | 
					            }))
 | 
				
			||||||
                .And(x => x.GivenTheConfigIsValid())
 | 
					                .And(x => x.GivenTheConfigIsValid())
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions))
 | 
				
			||||||
                .And(x => x.GivenTheLoadBalancerFactoryReturns())
 | 
					                .And(x => x.GivenTheLoadBalancerFactoryReturns())
 | 
				
			||||||
                .When(x => x.WhenICreateTheConfig())
 | 
					                .When(x => x.WhenICreateTheConfig())
 | 
				
			||||||
                .Then(x => x.ThenTheReRoutesAre(expected))
 | 
					                .Then(x => x.ThenTheReRoutesAre(expected))
 | 
				
			||||||
                .And(x => x.ThenTheAuthenticationOptionsAre(expected))
 | 
					                .And(x => x.ThenTheAuthenticationOptionsAre(expected))
 | 
				
			||||||
 | 
					                .And(x => x.ThenTheAuthOptionsCreatorIsCalledCorrectly())
 | 
				
			||||||
                .BDDfy();
 | 
					                .BDDfy();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Fact]
 | 
					        private void GivenTheFollowingOptionsAreReturned(ReRouteOptions fileReRouteOptions)
 | 
				
			||||||
        public void should_create_template_pattern_that_matches_more_than_one_placeholder()
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            this.Given(x => x.GivenTheConfigIs(new FileConfiguration
 | 
					            _fileReRouteOptionsCreator
 | 
				
			||||||
            {
 | 
					                .Setup(x => x.Create(It.IsAny<FileReRoute>()))
 | 
				
			||||||
                ReRoutes = new List<FileReRoute>
 | 
					                .Returns(fileReRouteOptions);
 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    new FileReRoute
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        UpstreamPathTemplate = "/api/products/{productId}/variants/{variantId}",
 | 
					 | 
				
			||||||
                        DownstreamPathTemplate = "/products/{productId}",
 | 
					 | 
				
			||||||
                        UpstreamHttpMethod = "Get",
 | 
					 | 
				
			||||||
                        ReRouteIsCaseSensitive = true
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }))
 | 
					 | 
				
			||||||
                .And(x => x.GivenTheConfigIsValid())
 | 
					 | 
				
			||||||
                .When(x => x.WhenICreateTheConfig())
 | 
					 | 
				
			||||||
                .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    new ReRouteBuilder()
 | 
					 | 
				
			||||||
                        .WithDownstreamPathTemplate("/products/{productId}")
 | 
					 | 
				
			||||||
                        .WithUpstreamPathTemplate("/api/products/{productId}/variants/{variantId}")
 | 
					 | 
				
			||||||
                        .WithUpstreamHttpMethod("Get")
 | 
					 | 
				
			||||||
                        .WithUpstreamTemplatePattern("/api/products/.*/variants/.*/$")
 | 
					 | 
				
			||||||
                        .Build()
 | 
					 | 
				
			||||||
                }))
 | 
					 | 
				
			||||||
                .BDDfy();
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Fact]
 | 
					        private void ThenTheRateLimitOptionsCreatorIsCalledCorrectly()
 | 
				
			||||||
        public void should_create_template_pattern_that_matches_more_than_one_placeholder_with_trailing_slash()
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            this.Given(x => x.GivenTheConfigIs(new FileConfiguration
 | 
					            _rateLimitOptions
 | 
				
			||||||
            {
 | 
					                .Verify(x => x.Create(It.IsAny<FileReRoute>(), It.IsAny<FileGlobalConfiguration>(), It.IsAny<bool>()), Times.Once);
 | 
				
			||||||
                ReRoutes = new List<FileReRoute>
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    new FileReRoute
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        UpstreamPathTemplate = "/api/products/{productId}/variants/{variantId}/",
 | 
					 | 
				
			||||||
                        DownstreamPathTemplate = "/products/{productId}",
 | 
					 | 
				
			||||||
                        UpstreamHttpMethod = "Get",
 | 
					 | 
				
			||||||
                        ReRouteIsCaseSensitive = true
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }))
 | 
					 | 
				
			||||||
                .And(x => x.GivenTheConfigIsValid())
 | 
					 | 
				
			||||||
                .When(x => x.WhenICreateTheConfig())
 | 
					 | 
				
			||||||
                .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    new ReRouteBuilder()
 | 
					 | 
				
			||||||
                        .WithDownstreamPathTemplate("/products/{productId}")
 | 
					 | 
				
			||||||
                        .WithUpstreamPathTemplate("/api/products/{productId}/variants/{variantId}/")
 | 
					 | 
				
			||||||
                        .WithUpstreamHttpMethod("Get")
 | 
					 | 
				
			||||||
                        .WithUpstreamTemplatePattern("/api/products/.*/variants/.*/$")
 | 
					 | 
				
			||||||
                        .Build()
 | 
					 | 
				
			||||||
                }))
 | 
					 | 
				
			||||||
                .BDDfy();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [Fact]
 | 
					 | 
				
			||||||
        public void should_create_template_pattern_that_matches_to_end_of_string()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            this.Given(x => x.GivenTheConfigIs(new FileConfiguration
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                ReRoutes = new List<FileReRoute>
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    new FileReRoute
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        UpstreamPathTemplate = "/",
 | 
					 | 
				
			||||||
                        DownstreamPathTemplate = "/api/products/",
 | 
					 | 
				
			||||||
                        UpstreamHttpMethod = "Get",
 | 
					 | 
				
			||||||
                        ReRouteIsCaseSensitive = true
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }))
 | 
					 | 
				
			||||||
                .And(x => x.GivenTheConfigIsValid())
 | 
					 | 
				
			||||||
                .When(x => x.WhenICreateTheConfig())
 | 
					 | 
				
			||||||
                .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    new ReRouteBuilder()
 | 
					 | 
				
			||||||
                        .WithDownstreamPathTemplate("/api/products/")
 | 
					 | 
				
			||||||
                        .WithUpstreamPathTemplate("/")
 | 
					 | 
				
			||||||
                        .WithUpstreamHttpMethod("Get")
 | 
					 | 
				
			||||||
                        .WithUpstreamTemplatePattern("^/$")
 | 
					 | 
				
			||||||
                        .Build()
 | 
					 | 
				
			||||||
                }))
 | 
					 | 
				
			||||||
                .BDDfy();
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void GivenTheConfigIsValid()
 | 
					        private void GivenTheConfigIsValid()
 | 
				
			||||||
@@ -642,6 +563,25 @@ namespace Ocelot.UnitTests.Configuration
 | 
				
			|||||||
                result.UpstreamHttpMethod.ShouldBe(expected.UpstreamHttpMethod);
 | 
					                result.UpstreamHttpMethod.ShouldBe(expected.UpstreamHttpMethod);
 | 
				
			||||||
                result.UpstreamPathTemplate.Value.ShouldBe(expected.UpstreamPathTemplate.Value);
 | 
					                result.UpstreamPathTemplate.Value.ShouldBe(expected.UpstreamPathTemplate.Value);
 | 
				
			||||||
                result.UpstreamTemplatePattern.ShouldBe(expected.UpstreamTemplatePattern);
 | 
					                result.UpstreamTemplatePattern.ShouldBe(expected.UpstreamTemplatePattern);
 | 
				
			||||||
 | 
					                result.ClaimsToClaims.Count.ShouldBe(expected.ClaimsToClaims.Count);
 | 
				
			||||||
 | 
					                result.ClaimsToHeaders.Count.ShouldBe(expected.ClaimsToHeaders.Count);
 | 
				
			||||||
 | 
					                result.ClaimsToQueries.Count.ShouldBe(expected.ClaimsToQueries.Count);
 | 
				
			||||||
 | 
					                result.RequestIdKey.ShouldBe(expected.RequestIdKey);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void ThenTheServiceConfigurationIs(ServiceProviderConfiguration expected)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            for (int i = 0; i < _config.Data.ReRoutes.Count; i++)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var result = _config.Data.ReRoutes[i];
 | 
				
			||||||
 | 
					                result.ServiceProviderConfiguraion.DownstreamHost.ShouldBe(expected.DownstreamHost);
 | 
				
			||||||
 | 
					                result.ServiceProviderConfiguraion.DownstreamPort.ShouldBe(expected.DownstreamPort);
 | 
				
			||||||
 | 
					                result.ServiceProviderConfiguraion.ServiceDiscoveryProvider.ShouldBe(expected.ServiceDiscoveryProvider);
 | 
				
			||||||
 | 
					                result.ServiceProviderConfiguraion.ServiceName.ShouldBe(expected.ServiceName);
 | 
				
			||||||
 | 
					                result.ServiceProviderConfiguraion.ServiceProviderHost.ShouldBe(expected.ServiceProviderHost);
 | 
				
			||||||
 | 
					                result.ServiceProviderConfiguraion.ServiceProviderPort.ShouldBe(expected.ServiceProviderPort);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -699,5 +639,60 @@ namespace Ocelot.UnitTests.Configuration
 | 
				
			|||||||
            _qosProviderHouse
 | 
					            _qosProviderHouse
 | 
				
			||||||
                .Verify(x => x.Add(It.IsAny<string>(), _qosProvider.Object), Times.Once);
 | 
					                .Verify(x => x.Add(It.IsAny<string>(), _qosProvider.Object), Times.Once);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenTheClaimsToThingCreatorReturns(List<ClaimToThing> claimsToThing)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _claimsToThingCreator
 | 
				
			||||||
 | 
					                .Setup(x => x.Create(_fileConfiguration.ReRoutes[0].AddHeadersToRequest))
 | 
				
			||||||
 | 
					                .Returns(claimsToThing);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenTheAuthOptionsCreatorReturns(AuthenticationOptions authOptions)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _authOptionsCreator
 | 
				
			||||||
 | 
					                .Setup(x => x.Create(It.IsAny<FileReRoute>()))
 | 
				
			||||||
 | 
					                .Returns(authOptions);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void ThenTheAuthOptionsCreatorIsCalledCorrectly()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _authOptionsCreator
 | 
				
			||||||
 | 
					                .Verify(x => x.Create(_fileConfiguration.ReRoutes[0]), Times.Once);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenTheUpstreamTemplatePatternCreatorReturns(string pattern)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _upstreamTemplatePatternCreator
 | 
				
			||||||
 | 
					                .Setup(x => x.Create(It.IsAny<FileReRoute>()))
 | 
				
			||||||
 | 
					                .Returns(pattern);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void ThenTheRequestIdKeyCreatorIsCalledCorrectly()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _requestIdKeyCreator
 | 
				
			||||||
 | 
					                .Verify(x => x.Create(_fileConfiguration.ReRoutes[0], _fileConfiguration.GlobalConfiguration), Times.Once);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenTheRequestIdCreatorReturns(string requestId)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _requestIdKeyCreator
 | 
				
			||||||
 | 
					                .Setup(x => x.Create(It.IsAny<FileReRoute>(), It.IsAny<FileGlobalConfiguration>()))
 | 
				
			||||||
 | 
					                .Returns(requestId);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenTheQosOptionsCreatorReturns(QoSOptions qosOptions)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _qosOptionsCreator
 | 
				
			||||||
 | 
					                .Setup(x => x.Create(_fileConfiguration.ReRoutes[0]))
 | 
				
			||||||
 | 
					                .Returns(qosOptions);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void ThenTheQosOptionsAre(QoSOptions qosOptions)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _config.Data.ReRoutes[0].QosOptions.DurationOfBreak.ShouldBe(qosOptions.DurationOfBreak);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _config.Data.ReRoutes[0].QosOptions.ExceptionsAllowedBeforeBreaking.ShouldBe(qosOptions.ExceptionsAllowedBeforeBreaking);
 | 
				
			||||||
 | 
					            _config.Data.ReRoutes[0].QosOptions.TimeoutValue.ShouldBe(qosOptions.TimeoutValue);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					using Ocelot.Configuration;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Builder;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Creator;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					using Shouldly;
 | 
				
			||||||
 | 
					using TestStack.BDDfy;
 | 
				
			||||||
 | 
					using Xunit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.UnitTests.Configuration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class QoSOptionsCreatorTests
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private QoSOptionsCreator _creator;
 | 
				
			||||||
 | 
					        private FileReRoute _fileReRoute;
 | 
				
			||||||
 | 
					        private QoSOptions _result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public QoSOptionsCreatorTests()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _creator = new QoSOptionsCreator();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_create_qos_options()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var reRoute = new FileReRoute
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                QoSOptions = new FileQoSOptions
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    ExceptionsAllowedBeforeBreaking = 1, 
 | 
				
			||||||
 | 
					                    DurationOfBreak = 1,
 | 
				
			||||||
 | 
					                    TimeoutValue = 1
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            var expected = new QoSOptionsBuilder()
 | 
				
			||||||
 | 
					                .WithDurationOfBreak(1)
 | 
				
			||||||
 | 
					                .WithExceptionsAllowedBeforeBreaking(1)
 | 
				
			||||||
 | 
					                .WithTimeoutValue(1)
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenTheFollowingReRoute(reRoute))
 | 
				
			||||||
 | 
					                .When(x => x.WhenICreate())
 | 
				
			||||||
 | 
					                .Then(x => x.ThenTheFollowingIsReturned(expected))
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenTheFollowingReRoute(FileReRoute fileReRoute)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _fileReRoute = fileReRoute;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void WhenICreate()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _result = _creator.Create(_fileReRoute);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void ThenTheFollowingIsReturned(QoSOptions expected)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _result.DurationOfBreak.ShouldBe(expected.DurationOfBreak);
 | 
				
			||||||
 | 
					            _result.ExceptionsAllowedBeforeBreaking.ShouldBe(expected.ExceptionsAllowedBeforeBreaking);
 | 
				
			||||||
 | 
					            _result.TimeoutValue.ShouldBe(expected.TimeoutValue);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,108 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Ocelot.Configuration;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Builder;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Creator;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					using Shouldly;
 | 
				
			||||||
 | 
					using TestStack.BDDfy;
 | 
				
			||||||
 | 
					using Xunit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.UnitTests.Configuration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class RateLimitOptionsCreatorTests
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private FileReRoute _fileReRoute;
 | 
				
			||||||
 | 
					        private FileGlobalConfiguration _fileGlobalConfig;
 | 
				
			||||||
 | 
					        private bool _enabled;
 | 
				
			||||||
 | 
					        private RateLimitOptionsCreator _creator;
 | 
				
			||||||
 | 
					        private RateLimitOptions _result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public RateLimitOptionsCreatorTests()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _creator = new RateLimitOptionsCreator();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_create_rate_limit_options()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var fileReRoute = new FileReRoute
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                RateLimitOptions = new FileRateLimitRule
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    ClientWhitelist = new List<string>(),
 | 
				
			||||||
 | 
					                    Period = "Period",
 | 
				
			||||||
 | 
					                    Limit = 1,
 | 
				
			||||||
 | 
					                    PeriodTimespan = 1,
 | 
				
			||||||
 | 
					                    EnableRateLimiting = true
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            var fileGlobalConfig = new FileGlobalConfiguration
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                RateLimitOptions = new FileRateLimitOptions
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    ClientIdHeader = "ClientIdHeader",
 | 
				
			||||||
 | 
					                    DisableRateLimitHeaders = true,
 | 
				
			||||||
 | 
					                    QuotaExceededMessage = "QuotaExceededMessage",
 | 
				
			||||||
 | 
					                    RateLimitCounterPrefix = "RateLimitCounterPrefix",
 | 
				
			||||||
 | 
					                    HttpStatusCode = 200
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            var expected = new RateLimitOptionsBuilder()
 | 
				
			||||||
 | 
					                .WithClientIdHeader("ClientIdHeader")
 | 
				
			||||||
 | 
					                .WithClientWhiteList(fileReRoute.RateLimitOptions.ClientWhitelist)
 | 
				
			||||||
 | 
					                .WithDisableRateLimitHeaders(true)
 | 
				
			||||||
 | 
					                .WithEnableRateLimiting(true)
 | 
				
			||||||
 | 
					                .WithHttpStatusCode(200)
 | 
				
			||||||
 | 
					                .WithQuotaExceededMessage("QuotaExceededMessage")
 | 
				
			||||||
 | 
					                .WithRateLimitCounterPrefix("RateLimitCounterPrefix")
 | 
				
			||||||
 | 
					                .WithRateLimitRule(new RateLimitRule(fileReRoute.RateLimitOptions.Period,
 | 
				
			||||||
 | 
					                       TimeSpan.FromSeconds(fileReRoute.RateLimitOptions.PeriodTimespan),
 | 
				
			||||||
 | 
					                       fileReRoute.RateLimitOptions.Limit))
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheFollowingFileGlobalConfig(fileGlobalConfig))
 | 
				
			||||||
 | 
					                .And(x => x.GivenRateLimitingIsEnabled())
 | 
				
			||||||
 | 
					                .When(x => x.WhenICreate())
 | 
				
			||||||
 | 
					                .Then(x => x.ThenTheFollowingIsReturned(expected))
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenTheFollowingFileReRoute(FileReRoute fileReRoute)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _fileReRoute = fileReRoute;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenTheFollowingFileGlobalConfig(FileGlobalConfiguration fileGlobalConfig)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _fileGlobalConfig = fileGlobalConfig;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenRateLimitingIsEnabled()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _enabled = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void WhenICreate()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _result = _creator.Create(_fileReRoute, _fileGlobalConfig, _enabled);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void ThenTheFollowingIsReturned(RateLimitOptions expected)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _result.ClientIdHeader.ShouldBe(expected.ClientIdHeader);
 | 
				
			||||||
 | 
					            _result.ClientWhitelist.ShouldBe(expected.ClientWhitelist);
 | 
				
			||||||
 | 
					            _result.DisableRateLimitHeaders.ShouldBe(expected.DisableRateLimitHeaders);
 | 
				
			||||||
 | 
					            _result.EnableRateLimiting.ShouldBe(expected.EnableRateLimiting);
 | 
				
			||||||
 | 
					            _result.HttpStatusCode.ShouldBe(expected.HttpStatusCode);
 | 
				
			||||||
 | 
					            _result.QuotaExceededMessage.ShouldBe(expected.QuotaExceededMessage);
 | 
				
			||||||
 | 
					            _result.RateLimitCounterPrefix.ShouldBe(expected.RateLimitCounterPrefix);
 | 
				
			||||||
 | 
					            _result.RateLimitRule.Limit.ShouldBe(expected.RateLimitRule.Limit);
 | 
				
			||||||
 | 
					            _result.RateLimitRule.Period.ShouldBe(expected.RateLimitRule.Period);
 | 
				
			||||||
 | 
					            _result.RateLimitRule.PeriodTimespan.Ticks.ShouldBe(expected.RateLimitRule.PeriodTimespan.Ticks);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,84 @@
 | 
				
			|||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using Ocelot.Configuration;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Builder;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Creator;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					using Shouldly;
 | 
				
			||||||
 | 
					using TestStack.BDDfy;
 | 
				
			||||||
 | 
					using Xunit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.UnitTests.Configuration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class ReRouteOptionsCreatorTests
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private ReRouteOptionsCreator _creator;
 | 
				
			||||||
 | 
					        private FileReRoute _reRoute;
 | 
				
			||||||
 | 
					        private ReRouteOptions _result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ReRouteOptionsCreatorTests()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _creator = new ReRouteOptionsCreator();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_create_re_route_options()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var reRoute = new FileReRoute
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                RateLimitOptions = new FileRateLimitRule
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    EnableRateLimiting = true
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                QoSOptions = new FileQoSOptions
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    ExceptionsAllowedBeforeBreaking = 1,
 | 
				
			||||||
 | 
					                    TimeoutValue = 1
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                AuthenticationOptions = new FileAuthenticationOptions
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    Provider = "IdentityServer"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                RouteClaimsRequirement = new Dictionary<string, string>()
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    {"",""}
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                FileCacheOptions = new FileCacheOptions
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    TtlSeconds = 1
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var expected = new ReRouteOptionsBuilder()
 | 
				
			||||||
 | 
					                .WithIsAuthenticated(true)
 | 
				
			||||||
 | 
					                .WithIsAuthorised(true)
 | 
				
			||||||
 | 
					                .WithIsCached(true)
 | 
				
			||||||
 | 
					                .WithIsQos(true)
 | 
				
			||||||
 | 
					                .WithRateLimiting(true)
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenTheFollowing(reRoute))
 | 
				
			||||||
 | 
					                .When(x => x.WhenICreate())
 | 
				
			||||||
 | 
					                .Then(x => x.ThenTheFollowingIsReturned(expected))
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenTheFollowing(FileReRoute reRoute)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _reRoute = reRoute;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void WhenICreate()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _result = _creator.Create(_reRoute);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void ThenTheFollowingIsReturned(ReRouteOptions expected)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _result.IsAuthenticated.ShouldBe(expected.IsAuthenticated);
 | 
				
			||||||
 | 
					            _result.IsAuthorised.ShouldBe(expected.IsAuthorised);
 | 
				
			||||||
 | 
					            _result.IsQos.ShouldBe(expected.IsQos);
 | 
				
			||||||
 | 
					            _result.IsCached.ShouldBe(expected.IsCached);
 | 
				
			||||||
 | 
					            _result.EnableRateLimiting.ShouldBe(expected.EnableRateLimiting);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,91 @@
 | 
				
			|||||||
 | 
					using Ocelot.Configuration.Creator;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					using Shouldly;
 | 
				
			||||||
 | 
					using TestStack.BDDfy;
 | 
				
			||||||
 | 
					using Xunit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.UnitTests.Configuration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class RequestIdKeyCreatorTests
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private FileReRoute _fileReRoute;
 | 
				
			||||||
 | 
					        private FileGlobalConfiguration _fileGlobalConfig;
 | 
				
			||||||
 | 
					        private string _result;
 | 
				
			||||||
 | 
					        private RequestIdKeyCreator _creator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public RequestIdKeyCreatorTests()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _creator = new RequestIdKeyCreator();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_use_global_configuration()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var reRoute = new FileReRoute();
 | 
				
			||||||
 | 
					            var globalConfig = new FileGlobalConfiguration
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                RequestIdKey = "cheese"
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenTheFollowingReRoute(reRoute))
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheFollowingGlobalConfig(globalConfig))
 | 
				
			||||||
 | 
					                .When(x => x.WhenICreate())
 | 
				
			||||||
 | 
					                .Then(x => x.ThenTheFollowingIsReturned("cheese"))
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_use_re_route_specific()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var reRoute = new FileReRoute
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                RequestIdKey = "cheese"
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            var globalConfig = new FileGlobalConfiguration();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenTheFollowingReRoute(reRoute))
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheFollowingGlobalConfig(globalConfig))
 | 
				
			||||||
 | 
					                .When(x => x.WhenICreate())
 | 
				
			||||||
 | 
					                .Then(x => x.ThenTheFollowingIsReturned("cheese"))
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_use_global_cofiguration_over_re_route_specific()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var reRoute = new FileReRoute
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                RequestIdKey = "cheese"
 | 
				
			||||||
 | 
					            };            var globalConfig = new FileGlobalConfiguration
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                RequestIdKey = "cheese"
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenTheFollowingReRoute(reRoute))
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheFollowingGlobalConfig(globalConfig))
 | 
				
			||||||
 | 
					                .When(x => x.WhenICreate())
 | 
				
			||||||
 | 
					                .Then(x => x.ThenTheFollowingIsReturned("cheese"))
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenTheFollowingReRoute(FileReRoute fileReRoute)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _fileReRoute = fileReRoute;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenTheFollowingGlobalConfig(FileGlobalConfiguration globalConfig)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _fileGlobalConfig = globalConfig;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void WhenICreate()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _result = _creator.Create(_fileReRoute, _fileGlobalConfig);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void ThenTheFollowingIsReturned(string expected)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _result.ShouldBe(expected);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,76 @@
 | 
				
			|||||||
 | 
					using Ocelot.Configuration;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Builder;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Creator;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					using Shouldly;
 | 
				
			||||||
 | 
					using TestStack.BDDfy;
 | 
				
			||||||
 | 
					using Xunit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.UnitTests.Configuration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class ServiceProviderCreatorTests
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private ServiceProviderConfigurationCreator _creator;
 | 
				
			||||||
 | 
					        private FileReRoute _reRoute;
 | 
				
			||||||
 | 
					        private FileGlobalConfiguration _globalConfig;
 | 
				
			||||||
 | 
					        private ServiceProviderConfiguration _result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ServiceProviderCreatorTests()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _creator = new ServiceProviderConfigurationCreator();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_create_service_provider_config()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var reRoute = new FileReRoute();
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            var globalConfig = new FileGlobalConfiguration
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    Provider = "consul",
 | 
				
			||||||
 | 
					                    Host = "127.0.0.1",
 | 
				
			||||||
 | 
					                    Port = 1234
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var expected = new ServiceProviderConfigurationBuilder()
 | 
				
			||||||
 | 
					                .WithServiceDiscoveryProvider("consul")
 | 
				
			||||||
 | 
					                .WithServiceDiscoveryProviderHost("127.0.0.1")
 | 
				
			||||||
 | 
					                .WithServiceDiscoveryProviderPort(1234)
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenTheFollowingReRoute(reRoute))
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheFollowingGlobalConfig(globalConfig))
 | 
				
			||||||
 | 
					                .When(x => x.WhenICreate())
 | 
				
			||||||
 | 
					                .Then(x => x.ThenTheConfigIs(expected))
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenTheFollowingReRoute(FileReRoute fileReRoute)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _reRoute = fileReRoute;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenTheFollowingGlobalConfig(FileGlobalConfiguration fileGlobalConfig)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _globalConfig = fileGlobalConfig;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void WhenICreate()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _result = _creator.Create(_reRoute, _globalConfig);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void ThenTheConfigIs(ServiceProviderConfiguration expected)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _result.DownstreamHost.ShouldBe(expected.DownstreamHost);
 | 
				
			||||||
 | 
					            _result.DownstreamPort.ShouldBe(expected.DownstreamPort);
 | 
				
			||||||
 | 
					            _result.ServiceDiscoveryProvider.ShouldBe(expected.ServiceDiscoveryProvider);
 | 
				
			||||||
 | 
					            _result.ServiceName.ShouldBe(expected.ServiceName);
 | 
				
			||||||
 | 
					            _result.ServiceProviderHost.ShouldBe(expected.ServiceProviderHost);
 | 
				
			||||||
 | 
					            _result.ServiceProviderPort.ShouldBe(expected.ServiceProviderPort);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,122 @@
 | 
				
			|||||||
 | 
					using Ocelot.Configuration.Creator;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					using Shouldly;
 | 
				
			||||||
 | 
					using TestStack.BDDfy;
 | 
				
			||||||
 | 
					using Xunit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.UnitTests.Configuration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class UpstreamTemplatePatternCreatorTests
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private FileReRoute _fileReRoute;
 | 
				
			||||||
 | 
					        private UpstreamTemplatePatternCreator _creator;
 | 
				
			||||||
 | 
					        private string _result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public UpstreamTemplatePatternCreatorTests()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _creator = new UpstreamTemplatePatternCreator();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_set_upstream_template_pattern_to_ignore_case_sensitivity()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var fileReRoute = new FileReRoute
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                UpstreamPathTemplate = "/PRODUCTS/{productId}",
 | 
				
			||||||
 | 
					                ReRouteIsCaseSensitive = false
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
 | 
				
			||||||
 | 
					                .When(x => x.WhenICreateTheTemplatePattern())
 | 
				
			||||||
 | 
					                .Then(x => x.ThenTheFollowingIsReturned("(?i)/PRODUCTS/.*/$"))
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_set_upstream_template_pattern_to_respect_case_sensitivity()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					                var fileReRoute = new FileReRoute
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    UpstreamPathTemplate = "/PRODUCTS/{productId}",
 | 
				
			||||||
 | 
					                    ReRouteIsCaseSensitive = true
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
 | 
				
			||||||
 | 
					                .When(x => x.WhenICreateTheTemplatePattern())
 | 
				
			||||||
 | 
					                .Then(x => x.ThenTheFollowingIsReturned("/PRODUCTS/.*/$"))
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_create_template_pattern_that_matches_anything_to_end_of_string()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var fileReRoute =  new FileReRoute
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                UpstreamPathTemplate = "/api/products/{productId}",
 | 
				
			||||||
 | 
					                ReRouteIsCaseSensitive = true
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
 | 
				
			||||||
 | 
					                .When(x => x.WhenICreateTheTemplatePattern())
 | 
				
			||||||
 | 
					                .Then(x => x.ThenTheFollowingIsReturned("/api/products/.*/$"))
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_create_template_pattern_that_matches_more_than_one_placeholder()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var fileReRoute = new FileReRoute
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                UpstreamPathTemplate = "/api/products/{productId}/variants/{variantId}",
 | 
				
			||||||
 | 
					                ReRouteIsCaseSensitive = true
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
 | 
				
			||||||
 | 
					                .When(x => x.WhenICreateTheTemplatePattern())
 | 
				
			||||||
 | 
					                .Then(x => x.ThenTheFollowingIsReturned("/api/products/.*/variants/.*/$"))
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_create_template_pattern_that_matches_more_than_one_placeholder_with_trailing_slash()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var fileReRoute = new FileReRoute
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                UpstreamPathTemplate = "/api/products/{productId}/variants/{variantId}/",
 | 
				
			||||||
 | 
					                ReRouteIsCaseSensitive = true
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
 | 
				
			||||||
 | 
					                .When(x => x.WhenICreateTheTemplatePattern())
 | 
				
			||||||
 | 
					                .Then(x => x.ThenTheFollowingIsReturned("/api/products/.*/variants/.*/$"))
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_create_template_pattern_that_matches_to_end_of_string()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var fileReRoute = new FileReRoute
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                UpstreamPathTemplate = "/"
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
 | 
				
			||||||
 | 
					                .When(x => x.WhenICreateTheTemplatePattern())
 | 
				
			||||||
 | 
					                .Then(x => x.ThenTheFollowingIsReturned("^/$"))
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenTheFollowingFileReRoute(FileReRoute fileReRoute)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _fileReRoute = fileReRoute;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void WhenICreateTheTemplatePattern()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _result = _creator.Create(_fileReRoute);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void ThenTheFollowingIsReturned(string expected)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _result.ShouldBe(expected);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,3 @@
 | 
				
			|||||||
using System;
 | 
					 | 
				
			||||||
using System.Net;
 | 
					 | 
				
			||||||
using Microsoft.AspNetCore.Mvc;
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
using Moq;
 | 
					using Moq;
 | 
				
			||||||
using Ocelot.Configuration.File;
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,7 +28,7 @@ namespace Ocelot.UnitTests.LoadBalancer
 | 
				
			|||||||
        public void should_return_no_load_balancer()
 | 
					        public void should_return_no_load_balancer()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var reRoute = new ReRouteBuilder()
 | 
					            var reRoute = new ReRouteBuilder()
 | 
				
			||||||
                .WithServiceProviderConfiguraion(new ServiceProviderConfiguraionBuilder().Build())
 | 
					                .WithServiceProviderConfiguraion(new ServiceProviderConfigurationBuilder().Build())
 | 
				
			||||||
                .WithUpstreamHttpMethod("Get")
 | 
					                .WithUpstreamHttpMethod("Get")
 | 
				
			||||||
                .Build();
 | 
					                .Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -45,7 +45,7 @@ namespace Ocelot.UnitTests.LoadBalancer
 | 
				
			|||||||
             var reRoute = new ReRouteBuilder()
 | 
					             var reRoute = new ReRouteBuilder()
 | 
				
			||||||
                .WithLoadBalancer("RoundRobin")
 | 
					                .WithLoadBalancer("RoundRobin")
 | 
				
			||||||
                .WithUpstreamHttpMethod("Get")
 | 
					                .WithUpstreamHttpMethod("Get")
 | 
				
			||||||
                .WithServiceProviderConfiguraion(new ServiceProviderConfiguraionBuilder().Build())
 | 
					                .WithServiceProviderConfiguraion(new ServiceProviderConfigurationBuilder().Build())
 | 
				
			||||||
                .Build();
 | 
					                .Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.Given(x => x.GivenAReRoute(reRoute))
 | 
					            this.Given(x => x.GivenAReRoute(reRoute))
 | 
				
			||||||
@@ -61,7 +61,7 @@ namespace Ocelot.UnitTests.LoadBalancer
 | 
				
			|||||||
             var reRoute = new ReRouteBuilder()
 | 
					             var reRoute = new ReRouteBuilder()
 | 
				
			||||||
                .WithLoadBalancer("LeastConnection")
 | 
					                .WithLoadBalancer("LeastConnection")
 | 
				
			||||||
                .WithUpstreamHttpMethod("Get")
 | 
					                .WithUpstreamHttpMethod("Get")
 | 
				
			||||||
                .WithServiceProviderConfiguraion(new ServiceProviderConfiguraionBuilder().Build())
 | 
					                .WithServiceProviderConfiguraion(new ServiceProviderConfigurationBuilder().Build())
 | 
				
			||||||
                .Build();
 | 
					                .Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.Given(x => x.GivenAReRoute(reRoute))
 | 
					            this.Given(x => x.GivenAReRoute(reRoute))
 | 
				
			||||||
@@ -77,7 +77,7 @@ namespace Ocelot.UnitTests.LoadBalancer
 | 
				
			|||||||
            var reRoute = new ReRouteBuilder()
 | 
					            var reRoute = new ReRouteBuilder()
 | 
				
			||||||
                .WithLoadBalancer("RoundRobin")
 | 
					                .WithLoadBalancer("RoundRobin")
 | 
				
			||||||
                .WithUpstreamHttpMethod("Get")
 | 
					                .WithUpstreamHttpMethod("Get")
 | 
				
			||||||
                .WithServiceProviderConfiguraion(new ServiceProviderConfiguraionBuilder().Build())
 | 
					                .WithServiceProviderConfiguraion(new ServiceProviderConfigurationBuilder().Build())
 | 
				
			||||||
                .Build();
 | 
					                .Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.Given(x => x.GivenAReRoute(reRoute))
 | 
					            this.Given(x => x.GivenAReRoute(reRoute))
 | 
				
			||||||
@@ -90,14 +90,14 @@ namespace Ocelot.UnitTests.LoadBalancer
 | 
				
			|||||||
        private void GivenTheServiceProviderFactoryReturns()
 | 
					        private void GivenTheServiceProviderFactoryReturns()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _serviceProviderFactory
 | 
					            _serviceProviderFactory
 | 
				
			||||||
                .Setup(x => x.Get(It.IsAny<ServiceProviderConfiguraion>()))
 | 
					                .Setup(x => x.Get(It.IsAny<ServiceProviderConfiguration>()))
 | 
				
			||||||
                .Returns(_serviceProvider.Object);
 | 
					                .Returns(_serviceProvider.Object);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void ThenTheServiceProviderIsCalledCorrectly()
 | 
					        private void ThenTheServiceProviderIsCalledCorrectly()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _serviceProviderFactory
 | 
					            _serviceProviderFactory
 | 
				
			||||||
                .Verify(x => x.Get(It.IsAny<ServiceProviderConfiguraion>()), Times.Once);
 | 
					                .Verify(x => x.Get(It.IsAny<ServiceProviderConfiguration>()), Times.Once);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void GivenAReRoute(ReRoute reRoute)
 | 
					        private void GivenAReRoute(ReRoute reRoute)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -79,7 +79,7 @@ namespace Ocelot.UnitTests.Request
 | 
				
			|||||||
            this.Given(x => x.GivenTheDownStreamUrlIs("any old string"))
 | 
					            this.Given(x => x.GivenTheDownStreamUrlIs("any old string"))
 | 
				
			||||||
                .And(x => x.GivenTheQosProviderHouseReturns(new OkResponse<IQoSProvider>(new NoQoSProvider())))
 | 
					                .And(x => x.GivenTheQosProviderHouseReturns(new OkResponse<IQoSProvider>(new NoQoSProvider())))
 | 
				
			||||||
                .And(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
 | 
					                .And(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
 | 
				
			||||||
                .And(x => x.GivenTheRequestBuilderReturns(new Ocelot.Request.Request(new HttpRequestMessage(), new CookieContainer(), true, new NoQoSProvider())))
 | 
					                .And(x => x.GivenTheRequestBuilderReturns(new Ocelot.Request.Request(new HttpRequestMessage(), true, new NoQoSProvider())))
 | 
				
			||||||
                .When(x => x.WhenICallTheMiddleware())
 | 
					                .When(x => x.WhenICallTheMiddleware())
 | 
				
			||||||
                .Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly())
 | 
					                .Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly())
 | 
				
			||||||
                .BDDfy();
 | 
					                .BDDfy();
 | 
				
			||||||
@@ -105,7 +105,7 @@ namespace Ocelot.UnitTests.Request
 | 
				
			|||||||
            _request = new OkResponse<Ocelot.Request.Request>(request);
 | 
					            _request = new OkResponse<Ocelot.Request.Request>(request);
 | 
				
			||||||
            _requestBuilder
 | 
					            _requestBuilder
 | 
				
			||||||
                .Setup(x => x.Build(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Stream>(), It.IsAny<IHeaderDictionary>(), 
 | 
					                .Setup(x => x.Build(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Stream>(), It.IsAny<IHeaderDictionary>(), 
 | 
				
			||||||
                It.IsAny<IRequestCookieCollection>(), It.IsAny<QueryString>(), It.IsAny<string>(), It.IsAny<Ocelot.RequestId.RequestId>(),It.IsAny<bool>(), It.IsAny<IQoSProvider>()))
 | 
					                It.IsAny<QueryString>(), It.IsAny<string>(), It.IsAny<Ocelot.RequestId.RequestId>(),It.IsAny<bool>(), It.IsAny<IQoSProvider>()))
 | 
				
			||||||
                .ReturnsAsync(_request);
 | 
					                .ReturnsAsync(_request);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -195,23 +195,6 @@ namespace Ocelot.UnitTests.Request
 | 
				
			|||||||
            _qoSProvider = qoSProvider;
 | 
					            _qoSProvider = qoSProvider;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Fact]
 | 
					 | 
				
			||||||
        public void should_use_cookies()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            this.Given(x => x.GivenIHaveHttpMethod("GET"))
 | 
					 | 
				
			||||||
               .And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk"))
 | 
					 | 
				
			||||||
               .And(x => x.GivenTheCookiesAre(new RequestCookieCollection(new Dictionary<string, string>
 | 
					 | 
				
			||||||
               {
 | 
					 | 
				
			||||||
                   { "TheCookie","Monster" }
 | 
					 | 
				
			||||||
               })))
 | 
					 | 
				
			||||||
               .When(x => x.WhenICreateARequest())
 | 
					 | 
				
			||||||
               .And(x => x.ThenTheCorrectCookiesAreUsed(new RequestCookieCollection(new Dictionary<string, string>
 | 
					 | 
				
			||||||
               {
 | 
					 | 
				
			||||||
                   { "TheCookie","Monster" }
 | 
					 | 
				
			||||||
               })))
 | 
					 | 
				
			||||||
               .BDDfy();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
        public void should_user_query_string()
 | 
					        public void should_user_query_string()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -240,14 +223,14 @@ namespace Ocelot.UnitTests.Request
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private void ThenTheCorrectCookiesAreUsed(IRequestCookieCollection expected)
 | 
					        private void ThenTheCorrectCookiesAreUsed(IRequestCookieCollection expected)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var resultCookies = _result.Data.CookieContainer.GetCookies(new Uri(_downstreamUrl + _query));
 | 
					           /* var resultCookies = _result.Data.CookieContainer.GetCookies(new Uri(_downstreamUrl + _query));
 | 
				
			||||||
            var resultDictionary = resultCookies.Cast<Cookie>().ToDictionary(cook => cook.Name, cook => cook.Value);
 | 
					            var resultDictionary = resultCookies.Cast<Cookie>().ToDictionary(cook => cook.Name, cook => cook.Value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            foreach (var expectedCookie in expected)
 | 
					            foreach (var expectedCookie in expected)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var resultCookie = resultDictionary[expectedCookie.Key];
 | 
					                var resultCookie = resultDictionary[expectedCookie.Key];
 | 
				
			||||||
                resultCookie.ShouldBe(expectedCookie.Value);
 | 
					                resultCookie.ShouldBe(expectedCookie.Value);
 | 
				
			||||||
            }
 | 
					            }*/
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void GivenTheCookiesAre(IRequestCookieCollection cookies)
 | 
					        private void GivenTheCookiesAre(IRequestCookieCollection cookies)
 | 
				
			||||||
@@ -305,7 +288,7 @@ namespace Ocelot.UnitTests.Request
 | 
				
			|||||||
        private void WhenICreateARequest()
 | 
					        private void WhenICreateARequest()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _result = _requestCreator.Build(_httpMethod, _downstreamUrl, _content?.ReadAsStreamAsync().Result, _headers,
 | 
					            _result = _requestCreator.Build(_httpMethod, _downstreamUrl, _content?.ReadAsStreamAsync().Result, _headers,
 | 
				
			||||||
                _cookies, _query, _contentType, _requestId,_isQos,_qoSProvider).Result;
 | 
					                _query, _contentType, _requestId,_isQos,_qoSProvider).Result;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -62,7 +62,7 @@ namespace Ocelot.UnitTests.Requester
 | 
				
			|||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
        public void should_call_scoped_data_repository_correctly()
 | 
					        public void should_call_scoped_data_repository_correctly()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage(),new CookieContainer(),true, new NoQoSProvider())))
 | 
					            this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage(),true, new NoQoSProvider())))
 | 
				
			||||||
                .And(x => x.GivenTheRequesterReturns(new HttpResponseMessage()))
 | 
					                .And(x => x.GivenTheRequesterReturns(new HttpResponseMessage()))
 | 
				
			||||||
                .And(x => x.GivenTheScopedRepoReturns())
 | 
					                .And(x => x.GivenTheScopedRepoReturns())
 | 
				
			||||||
                .When(x => x.WhenICallTheMiddleware())
 | 
					                .When(x => x.WhenICallTheMiddleware())
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,7 @@ namespace Ocelot.UnitTests.ServiceDiscovery
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public class ServiceProviderFactoryTests
 | 
					    public class ServiceProviderFactoryTests
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private ServiceProviderConfiguraion _serviceConfig;
 | 
					        private ServiceProviderConfiguration _serviceConfig;
 | 
				
			||||||
        private IServiceDiscoveryProvider _result;
 | 
					        private IServiceDiscoveryProvider _result;
 | 
				
			||||||
        private readonly ServiceDiscoveryProviderFactory _factory;
 | 
					        private readonly ServiceDiscoveryProviderFactory _factory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -21,7 +21,7 @@ namespace Ocelot.UnitTests.ServiceDiscovery
 | 
				
			|||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
        public void should_return_no_service_provider()
 | 
					        public void should_return_no_service_provider()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var serviceConfig = new ServiceProviderConfiguraionBuilder()
 | 
					            var serviceConfig = new ServiceProviderConfigurationBuilder()
 | 
				
			||||||
                .WithDownstreamHost("127.0.0.1")
 | 
					                .WithDownstreamHost("127.0.0.1")
 | 
				
			||||||
                .WithDownstreamPort(80)
 | 
					                .WithDownstreamPort(80)
 | 
				
			||||||
                .WithUseServiceDiscovery(false)
 | 
					                .WithUseServiceDiscovery(false)
 | 
				
			||||||
@@ -36,7 +36,7 @@ namespace Ocelot.UnitTests.ServiceDiscovery
 | 
				
			|||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
        public void should_return_consul_service_provider()
 | 
					        public void should_return_consul_service_provider()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var serviceConfig = new ServiceProviderConfiguraionBuilder()
 | 
					            var serviceConfig = new ServiceProviderConfigurationBuilder()
 | 
				
			||||||
                .WithServiceName("product")
 | 
					                .WithServiceName("product")
 | 
				
			||||||
                .WithUseServiceDiscovery(true)
 | 
					                .WithUseServiceDiscovery(true)
 | 
				
			||||||
                .WithServiceDiscoveryProvider("Consul")
 | 
					                .WithServiceDiscoveryProvider("Consul")
 | 
				
			||||||
@@ -48,7 +48,7 @@ namespace Ocelot.UnitTests.ServiceDiscovery
 | 
				
			|||||||
                .BDDfy();
 | 
					                .BDDfy();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void GivenTheReRoute(ServiceProviderConfiguraion serviceConfig)
 | 
					        private void GivenTheReRoute(ServiceProviderConfiguration serviceConfig)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _serviceConfig = serviceConfig;
 | 
					            _serviceConfig = serviceConfig;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user