mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 06:22:50 +08:00
Merge branch 'develop' into feature/Optimization-HttpClient-instance
This commit is contained in:
commit
2e1708ef1e
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;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user