mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 10:12:51 +08:00
commit
2fded67de8
@ -13,7 +13,7 @@ osx_image: xcode9.2
|
||||
mono:
|
||||
- 4.4.2
|
||||
|
||||
dotnet: 2.1.4
|
||||
dotnet: 2.1.301
|
||||
|
||||
before_install:
|
||||
- git fetch --unshallow # Travis always does a shallow clone, but GitVersion needs the full history including branches and tags
|
||||
|
@ -55,7 +55,7 @@ A quick list of Ocelot's capabilities for more information see the [documentatio
|
||||
|
||||
## How to install
|
||||
|
||||
Ocelot is designed to work with ASP.NET Core only and it targets `netstandard2.0`. This means it can be used anywhere `.NET Standard 2.0` is supported, including `.NET Core 2.0` and `.NET Framework 4.6.1` and up. [This](https://docs.microsoft.com/en-us/dotnet/standard/net-standard) documentation may prove helpful when working out if Ocelot would be suitable for you.
|
||||
Ocelot is designed to work with ASP.NET Core only and it targets `netstandard2.0`. This means it can be used anywhere `.NET Standard 2.0` is supported, including `.NET Core 2.1` and `.NET Framework 4.7.2` and up. [This](https://docs.microsoft.com/en-us/dotnet/standard/net-standard) documentation may prove helpful when working out if Ocelot would be suitable for you.
|
||||
|
||||
Install Ocelot and it's dependencies using NuGet.
|
||||
|
||||
|
@ -1,144 +1,149 @@
|
||||
Authentication
|
||||
==============
|
||||
|
||||
In order to authenticate ReRoutes and subsequently use any of Ocelot's claims based features such as authorisation or modifying the request with values from the token. Users must register authentication services in their Startup.cs as usual but they provide a scheme (authentication provider key) with each registration e.g.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
var authenticationProviderKey = "TestKey";
|
||||
|
||||
services.AddAuthentication()
|
||||
.AddJwtBearer(authenticationProviderKey, x =>
|
||||
{
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
In this example TestKey is the scheme that this provider has been registered with.
|
||||
We then map this to a ReRoute in the configuration e.g.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
"ReRoutes": [{
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "localhost",
|
||||
"Port": 51876,
|
||||
}
|
||||
],
|
||||
"DownstreamPathTemplate": "/",
|
||||
"UpstreamPathTemplate": "/",
|
||||
"UpstreamHttpMethod": ["Post"],
|
||||
"ReRouteIsCaseSensitive": false,
|
||||
"DownstreamScheme": "http",
|
||||
"AuthenticationOptions": {
|
||||
"AuthenticationProviderKey": "TestKey",
|
||||
"AllowedScopes": []
|
||||
}
|
||||
}]
|
||||
|
||||
When Ocelot runs it will look at this ReRoutes AuthenticationOptions.AuthenticationProviderKey
|
||||
and check that there is an Authentication provider registered with the given key. If there isn't then Ocelot
|
||||
will not start up, if there is then the ReRoute will use that provider when it executes.
|
||||
|
||||
If a ReRoute is authenticated Ocelot will invoke whatever scheme is associated with it while executing the authentication middleware. If the request fails authentication Ocelot returns a http status code 401.
|
||||
|
||||
JWT Tokens
|
||||
^^^^^^^^^^
|
||||
|
||||
If you want to authenticate using JWT tokens maybe from a provider like Auth0 you can register your authentication middleware as normal e.g.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
var authenticationProviderKey = "TestKey";
|
||||
|
||||
services.AddAuthentication()
|
||||
.AddJwtBearer(authenticationProviderKey, x =>
|
||||
{
|
||||
x.Authority = "test";
|
||||
x.Audience = "test";
|
||||
});
|
||||
|
||||
services.AddOcelot();
|
||||
}
|
||||
|
||||
Then map the authentication provider key to a ReRoute in your configuration e.g.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
"ReRoutes": [{
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "localhost",
|
||||
"Port": 51876,
|
||||
}
|
||||
],
|
||||
"DownstreamPathTemplate": "/",
|
||||
"UpstreamPathTemplate": "/",
|
||||
"UpstreamHttpMethod": ["Post"],
|
||||
"ReRouteIsCaseSensitive": false,
|
||||
"DownstreamScheme": "http",
|
||||
"AuthenticationOptions": {
|
||||
"AuthenticationProviderKey": "TestKey",
|
||||
"AllowedScopes": []
|
||||
}
|
||||
}]
|
||||
|
||||
|
||||
|
||||
Identity Server Bearer Tokens
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In order to use IdentityServer bearer tokens register your IdentityServer services as usual in ConfigureServices with a scheme (key). If you don't understand how to do this please consult the IdentityServer documentation.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
var authenticationProviderKey = "TestKey";
|
||||
var options = o =>
|
||||
{
|
||||
o.Authority = "https://whereyouridentityserverlives.com";
|
||||
o.ApiName = "api";
|
||||
o.SupportedTokens = SupportedTokens.Both;
|
||||
o.ApiSecret = "secret";
|
||||
};
|
||||
|
||||
services.AddAuthentication()
|
||||
.AddIdentityServerAuthentication(authenticationProviderKey, options);
|
||||
|
||||
services.AddOcelot();
|
||||
}
|
||||
|
||||
Then map the authentication provider key to a ReRoute in your configuration e.g.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
"ReRoutes": [{
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "localhost",
|
||||
"Port": 51876,
|
||||
}
|
||||
],
|
||||
"DownstreamPathTemplate": "/",
|
||||
"UpstreamPathTemplate": "/",
|
||||
"UpstreamHttpMethod": ["Post"],
|
||||
"ReRouteIsCaseSensitive": false,
|
||||
"DownstreamScheme": "http",
|
||||
"AuthenticationOptions": {
|
||||
"AuthenticationProviderKey": "TestKey",
|
||||
"AllowedScopes": []
|
||||
}
|
||||
}]
|
||||
|
||||
Allowed Scopes
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
If you add scopes to AllowedScopes Ocelot will get all the user claims (from the token) of the type scope and make sure that the user has all of the scopes in the list.
|
||||
|
||||
This is a way to restrict access to a ReRoute on a per scope basis.
|
||||
Authentication
|
||||
==============
|
||||
|
||||
In order to authenticate ReRoutes and subsequently use any of Ocelot's claims based features such as authorisation or modifying the request with values from the token. Users must register authentication services in their Startup.cs as usual but they provide a scheme (authentication provider key) with each registration e.g.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
var authenticationProviderKey = "TestKey";
|
||||
|
||||
services.AddAuthentication()
|
||||
.AddJwtBearer(authenticationProviderKey, x =>
|
||||
{
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
In this example TestKey is the scheme that this provider has been registered with.
|
||||
We then map this to a ReRoute in the configuration e.g.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
"ReRoutes": [{
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "localhost",
|
||||
"Port": 51876,
|
||||
}
|
||||
],
|
||||
"DownstreamPathTemplate": "/",
|
||||
"UpstreamPathTemplate": "/",
|
||||
"UpstreamHttpMethod": ["Post"],
|
||||
"ReRouteIsCaseSensitive": false,
|
||||
"DownstreamScheme": "http",
|
||||
"AuthenticationOptions": {
|
||||
"AuthenticationProviderKey": "TestKey",
|
||||
"AllowedScopes": []
|
||||
}
|
||||
}]
|
||||
|
||||
When Ocelot runs it will look at this ReRoutes AuthenticationOptions.AuthenticationProviderKey
|
||||
and check that there is an Authentication provider registered with the given key. If there isn't then Ocelot
|
||||
will not start up, if there is then the ReRoute will use that provider when it executes.
|
||||
|
||||
If a ReRoute is authenticated Ocelot will invoke whatever scheme is associated with it while executing the authentication middleware. If the request fails authentication Ocelot returns a http status code 401.
|
||||
|
||||
JWT Tokens
|
||||
^^^^^^^^^^
|
||||
|
||||
If you want to authenticate using JWT tokens maybe from a provider like Auth0 you can register your authentication middleware as normal e.g.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
var authenticationProviderKey = "TestKey";
|
||||
|
||||
services.AddAuthentication()
|
||||
.AddJwtBearer(authenticationProviderKey, x =>
|
||||
{
|
||||
x.Authority = "test";
|
||||
x.Audience = "test";
|
||||
});
|
||||
|
||||
services.AddOcelot();
|
||||
}
|
||||
|
||||
Then map the authentication provider key to a ReRoute in your configuration e.g.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
"ReRoutes": [{
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "localhost",
|
||||
"Port": 51876,
|
||||
}
|
||||
],
|
||||
"DownstreamPathTemplate": "/",
|
||||
"UpstreamPathTemplate": "/",
|
||||
"UpstreamHttpMethod": ["Post"],
|
||||
"ReRouteIsCaseSensitive": false,
|
||||
"DownstreamScheme": "http",
|
||||
"AuthenticationOptions": {
|
||||
"AuthenticationProviderKey": "TestKey",
|
||||
"AllowedScopes": []
|
||||
}
|
||||
}]
|
||||
|
||||
|
||||
|
||||
Identity Server Bearer Tokens
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In order to use IdentityServer bearer tokens register your IdentityServer services as usual in ConfigureServices with a scheme (key). If you don't understand how to do this please consult the IdentityServer documentation.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
var authenticationProviderKey = "TestKey";
|
||||
Action<IdentityServerAuthenticationOptions> options = o =>
|
||||
{
|
||||
o.Authority = "https://whereyouridentityserverlives.com";
|
||||
o.ApiName = "api";
|
||||
o.SupportedTokens = SupportedTokens.Both;
|
||||
o.ApiSecret = "secret";
|
||||
};
|
||||
|
||||
services.AddAuthentication()
|
||||
.AddIdentityServerAuthentication(authenticationProviderKey, options);
|
||||
|
||||
services.AddOcelot();
|
||||
}
|
||||
|
||||
Then map the authentication provider key to a ReRoute in your configuration e.g.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
"ReRoutes": [{
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "localhost",
|
||||
"Port": 51876,
|
||||
}
|
||||
],
|
||||
"DownstreamPathTemplate": "/",
|
||||
"UpstreamPathTemplate": "/",
|
||||
"UpstreamHttpMethod": ["Post"],
|
||||
"ReRouteIsCaseSensitive": false,
|
||||
"DownstreamScheme": "http",
|
||||
"AuthenticationOptions": {
|
||||
"AuthenticationProviderKey": "TestKey",
|
||||
"AllowedScopes": []
|
||||
}
|
||||
}]
|
||||
|
||||
Okta
|
||||
^^^^
|
||||
|
||||
I have not had time to write this up but we have `Issue 446 <https://github.com/ThreeMammals/Ocelot/issues/446>`_ that contains some code and examples that might help with Okta integration.
|
||||
|
||||
Allowed Scopes
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
If you add scopes to AllowedScopes Ocelot will get all the user claims (from the token) of the type scope and make sure that the user has all of the scopes in the list.
|
||||
|
||||
This is a way to restrict access to a ReRoute on a per scope basis.
|
||||
|
@ -1,21 +1,34 @@
|
||||
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. You can also clear the cache for a region
|
||||
by calling Ocelot's administration API.
|
||||
|
||||
In order to use caching on a route in your ReRoute configuration add this setting.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
"FileCacheOptions": { "TtlSeconds": 15, "Region": "somename" }
|
||||
|
||||
In this example ttl seconds is set to 15 which means the cache will expire after 15 seconds.
|
||||
Caching
|
||||
=======
|
||||
|
||||
Ocelot supports some very rudimentary caching at the moment provider by
|
||||
the `CacheManager <https://github.com/MichaCo/CacheManager>`_ project. This is an amazing project
|
||||
that is solving a lot of caching problems. I would reccomend using this package to
|
||||
cache with Ocelot.
|
||||
|
||||
The following example shows how to add CacheManager to Ocelot so that you can do output caching. The first thing you need to do is add the following to your ConfigureServices..
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
s.AddOcelot()
|
||||
.AddCacheManager(x =>
|
||||
{
|
||||
x.WithDictionaryHandle();
|
||||
})
|
||||
|
||||
In order to use caching on a route in your ReRoute configuration add this setting.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
"FileCacheOptions": { "TtlSeconds": 15, "Region": "somename" }
|
||||
|
||||
In this example ttl seconds is set to 15 which means the cache will expire after 15 seconds.
|
||||
|
||||
If you look at the example `here <https://github.com/TomPallister/Ocelot/blob/develop/test/Ocelot.ManualTest/Program.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. You can also clear the cache for a region
|
||||
by calling Ocelot's administration API.
|
||||
|
||||
|
@ -1,79 +1,66 @@
|
||||
Delegating Handers
|
||||
==================
|
||||
|
||||
Ocelot allows the user to add delegating handlers to the HttpClient transport. This feature was requested `GitHub #208 <https://github.com/TomPallister/Ocelot/issues/208>`_
|
||||
and I decided that it was going to be useful in various ways. Since then we extended it in `GitHub #264 <https://github.com/TomPallister/Ocelot/issues/264>`_.
|
||||
|
||||
Usage
|
||||
^^^^^
|
||||
|
||||
In order to add delegating handlers to the HttpClient transport you need to do two main things.
|
||||
|
||||
First in order to create a class that can be used a delegating handler it must look as follows. We are going to register these handlers in the
|
||||
asp.net core container so you can inject any other services you have registered into the constructor of your handler.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
public class FakeHandler : DelegatingHandler
|
||||
{
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
//do stuff and optionally call the base handler..
|
||||
return await base.SendAsync(request, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
Next you must add the handlers to Ocelot's container either as singleton like follows..
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
services.AddOcelot()
|
||||
.AddSingletonDelegatingHandler<FakeHandler>()
|
||||
.AddSingletonDelegatingHandler<FakeHandlerTwo>()
|
||||
|
||||
Or transient as below...
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
services.AddOcelot()
|
||||
.AddTransientDelegatingHandler<FakeHandler>()
|
||||
.AddTransientDelegatingHandler<FakeHandlerTwo>()
|
||||
|
||||
Both of these Add methods have a default parameter called global which is set to false. If it is false then the intent of
|
||||
the DelegatingHandler is to be applied to specific ReRoutes via ocelot.json (more on that later). If it is set to true
|
||||
then it becomes a global handler and will be applied to all ReRoutes.
|
||||
|
||||
e.g.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
services.AddOcelot()
|
||||
.AddSingletonDelegatingHandler<FakeHandler>(true)
|
||||
|
||||
Or transient as below...
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
services.AddOcelot()
|
||||
.AddTransientDelegatingHandler<FakeHandler>(true)
|
||||
|
||||
Finally if you want ReRoute specific DelegatingHandlers or to order your specific and / or global (more on this later) DelegatingHandlers
|
||||
then you must add the following json to the specific ReRoute in ocelot.json. The names in the array must match the class names of your
|
||||
DelegatingHandlers for Ocelot to match them together.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
"DelegatingHandlers": [
|
||||
"FakeHandlerTwo",
|
||||
"FakeHandler"
|
||||
]
|
||||
|
||||
You can have as many DelegatingHandlers as you want and they are run in the following order:
|
||||
|
||||
1. Any globals that are left in the order they were added to services and are not in the DelegatingHandlers array from ocelot.json.
|
||||
2. Any non global DelegatingHandlers plus any globals that were in the DelegatingHandlers array from ocelot.json ordered as they are in the DelegatingHandlers array.
|
||||
3. Tracing DelegatingHandler if enabled (see tracing docs).
|
||||
4. QoS DelegatingHandler if enabled (see QoS docs).
|
||||
5. The HttpClient sends the HttpRequestMessage.
|
||||
|
||||
Hopefully other people will find this feature useful!
|
||||
Delegating Handlers
|
||||
===================
|
||||
|
||||
Ocelot allows the user to add delegating handlers to the HttpClient transport. This feature was requested `GitHub #208 <https://github.com/TomPallister/Ocelot/issues/208>`_
|
||||
and I decided that it was going to be useful in various ways. Since then we extended it in `GitHub #264 <https://github.com/TomPallister/Ocelot/issues/264>`_.
|
||||
|
||||
Usage
|
||||
^^^^^
|
||||
|
||||
In order to add delegating handlers to the HttpClient transport you need to do two main things.
|
||||
|
||||
First in order to create a class that can be used a delegating handler it must look as follows. We are going to register these handlers in the
|
||||
asp.net core container so you can inject any other services you have registered into the constructor of your handler.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
public class FakeHandler : DelegatingHandler
|
||||
{
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
//do stuff and optionally call the base handler..
|
||||
return await base.SendAsync(request, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
Next you must add the handlers to Ocelot's container like below...
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
services.AddOcelot()
|
||||
.AddDelegatingHandler<FakeHandler>()
|
||||
.AddDelegatingHandler<FakeHandlerTwo>()
|
||||
|
||||
Both of these Add methods have a default parameter called global which is set to false. If it is false then the intent of
|
||||
the DelegatingHandler is to be applied to specific ReRoutes via ocelot.json (more on that later). If it is set to true
|
||||
then it becomes a global handler and will be applied to all ReRoutes.
|
||||
|
||||
e.g.
|
||||
|
||||
As below...
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
services.AddOcelot()
|
||||
.AddDelegatingHandler<FakeHandler>(true)
|
||||
|
||||
Finally if you want ReRoute specific DelegatingHandlers or to order your specific and / or global (more on this later) DelegatingHandlers
|
||||
then you must add the following json to the specific ReRoute in ocelot.json. The names in the array must match the class names of your
|
||||
DelegatingHandlers for Ocelot to match them together.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
"DelegatingHandlers": [
|
||||
"FakeHandlerTwo",
|
||||
"FakeHandler"
|
||||
]
|
||||
|
||||
You can have as many DelegatingHandlers as you want and they are run in the following order:
|
||||
|
||||
1. Any globals that are left in the order they were added to services and are not in the DelegatingHandlers array from ocelot.json.
|
||||
2. Any non global DelegatingHandlers plus any globals that were in the DelegatingHandlers array from ocelot.json ordered as they are in the DelegatingHandlers array.
|
||||
3. Tracing DelegatingHandler if enabled (see tracing docs).
|
||||
4. QoS DelegatingHandler if enabled (see QoS docs).
|
||||
5. The HttpClient sends the HttpRequestMessage.
|
||||
|
||||
Hopefully other people will find this feature useful!
|
||||
|
@ -1,187 +1,247 @@
|
||||
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'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.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"ReRoutes": [
|
||||
]
|
||||
}
|
||||
|
||||
In order to set up a ReRoute you need to add one to the json array called ReRoutes like
|
||||
the following.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/posts/{postId}",
|
||||
"DownstreamScheme": "https",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "localhost",
|
||||
"Port": 80,
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts/{postId}",
|
||||
"UpstreamHttpMethod": [ "Put", "Delete" ]
|
||||
}
|
||||
|
||||
The DownstreamPathTemplate, Scheme and DownstreamHostAndPorts make the URL that this request will be forwarded to.
|
||||
|
||||
DownstreamHostAndPorts is an array that contains the host and port of any downstream services that you wish to forward requests to. Usually this will just contain one entry but sometimes you might want to load balance
|
||||
requests to your downstream services and Ocelot let's you add more than one entry and then select a load balancer.
|
||||
|
||||
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 :)
|
||||
|
||||
You can set a specific list of HTTP Methods or set an empty list to allow any of them. 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.
|
||||
|
||||
You can also do a catch all type of ReRoute e.g.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/{everything}",
|
||||
"DownstreamScheme": "https",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "localhost",
|
||||
"Port": 80,
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/{everything}",
|
||||
"UpstreamHttpMethod": [ "Get", "Post" ]
|
||||
}
|
||||
|
||||
This will forward any path + query string combinations to the downstream service after the path /api.
|
||||
|
||||
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.
|
||||
|
||||
.. code-block:: 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!
|
||||
|
||||
Catch All
|
||||
^^^^^^^^^
|
||||
|
||||
Ocelot's routing also supports a catch all style routing where the user can specify that they want to match all traffic if you set up your config like below the request will be proxied straight through (it doesnt have to be url any placeholder name will work).
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"DownstreamPathTemplate": "/{url}",
|
||||
"DownstreamScheme": "https",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "localhost",
|
||||
"Port": 80,
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/{url}",
|
||||
"UpstreamHttpMethod": [ "Get" ]
|
||||
}
|
||||
|
||||
The catch all has a lower priority than any other ReRoute. If you also have the ReRoute below in your config then Ocelot would match it before the catch all.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"DownstreamPathTemplate": "/",
|
||||
"DownstreamScheme": "https",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "10.0.10.1",
|
||||
"Port": 80,
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/",
|
||||
"UpstreamHttpMethod": [ "Get" ]
|
||||
}
|
||||
|
||||
Upstream Host
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
This feature allows you to have ReRoutes based on the upstream host. This works by looking at the host header the client has used and then using this as part of the information we use to identify a ReRoute.
|
||||
|
||||
In order to use this feature please add the following to your config.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"DownstreamPathTemplate": "/",
|
||||
"DownstreamScheme": "https",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "10.0.10.1",
|
||||
"Port": 80,
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"UpstreamHost": "somedomain.com"
|
||||
}
|
||||
|
||||
The ReRoute above will only be matched when the host header value is somedomain.com.
|
||||
|
||||
If you do not set UpstreamHost on a ReRoue then any host header can match it. This is basically a catch all and
|
||||
preservers existing functionality at the time of building the feature. This means that if you have two ReRoutes that are the same apart from the UpstreamHost where one is null and the other set. Ocelot will favour the one that has been set.
|
||||
|
||||
This feature was requested as part of `Issue 216 <https://github.com/TomPallister/Ocelot/pull/216>`_ .
|
||||
|
||||
Priority
|
||||
^^^^^^^^
|
||||
|
||||
In `Issue 270 <https://github.com/TomPallister/Ocelot/pull/270>`_ I finally decided to expose the ReRoute priority in
|
||||
ocelot.json. This means you can decide in what order you want your ReRoutes to match the Upstream HttpRequest.
|
||||
|
||||
In order to get this working add the following to a ReRoute in ocelot.json, 0 is just an example value here but will explain below.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"Priority": 0
|
||||
}
|
||||
|
||||
0 is the lowest priority, Ocelot will always use 0 for /{catchAll} ReRoutes and this is still hardcoded. After that you are free
|
||||
to set any priority you wish.
|
||||
|
||||
e.g. you could have
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"UpstreamPathTemplate": "/goods/{catchAll}"
|
||||
"Priority": 0
|
||||
}
|
||||
|
||||
and
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"UpstreamPathTemplate": "/goods/delete"
|
||||
"Priority": 1
|
||||
}
|
||||
|
||||
In the example above if you make a request into Ocelot on /goods/delete Ocelot will match /goods/delete ReRoute. Previously it would have
|
||||
matched /goods/{catchAll} (because this is the first ReRoute in the list!).
|
||||
|
||||
Dynamic Routing
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
This feature was requested in `issue 340 <https://github.com/TomPallister/Ocelot/issue/340>`_. The idea is to enable dynamic routing
|
||||
when using a service discovery provider so you don't have to provide the ReRoute config. See the docs :ref:`service-discovery` if
|
||||
this sounds interesting to you.
|
||||
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'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.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"ReRoutes": [
|
||||
]
|
||||
}
|
||||
|
||||
In order to set up a ReRoute you need to add one to the json array called ReRoutes like
|
||||
the following.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/posts/{postId}",
|
||||
"DownstreamScheme": "https",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "localhost",
|
||||
"Port": 80,
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts/{postId}",
|
||||
"UpstreamHttpMethod": [ "Put", "Delete" ]
|
||||
}
|
||||
|
||||
The DownstreamPathTemplate, Scheme and DownstreamHostAndPorts make the URL that this request will be forwarded to.
|
||||
|
||||
DownstreamHostAndPorts is an array that contains the host and port of any downstream services that you wish to forward requests to. Usually this will just contain one entry but sometimes you might want to load balance
|
||||
requests to your downstream services and Ocelot let's you add more than one entry and then select a load balancer.
|
||||
|
||||
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 :)
|
||||
|
||||
You can set a specific list of HTTP Methods or set an empty list to allow any of them. 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.
|
||||
|
||||
You can also do a catch all type of ReRoute e.g.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/{everything}",
|
||||
"DownstreamScheme": "https",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "localhost",
|
||||
"Port": 80,
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/{everything}",
|
||||
"UpstreamHttpMethod": [ "Get", "Post" ]
|
||||
}
|
||||
|
||||
This will forward any path + query string combinations to the downstream service after the path /api.
|
||||
|
||||
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.
|
||||
|
||||
.. code-block:: 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!
|
||||
|
||||
Catch All
|
||||
^^^^^^^^^
|
||||
|
||||
Ocelot's routing also supports a catch all style routing where the user can specify that they want to match all traffic if you set up your config like below the request will be proxied straight through (it doesnt have to be url any placeholder name will work).
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"DownstreamPathTemplate": "/{url}",
|
||||
"DownstreamScheme": "https",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "localhost",
|
||||
"Port": 80,
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/{url}",
|
||||
"UpstreamHttpMethod": [ "Get" ]
|
||||
}
|
||||
|
||||
The catch all has a lower priority than any other ReRoute. If you also have the ReRoute below in your config then Ocelot would match it before the catch all.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"DownstreamPathTemplate": "/",
|
||||
"DownstreamScheme": "https",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "10.0.10.1",
|
||||
"Port": 80,
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/",
|
||||
"UpstreamHttpMethod": [ "Get" ]
|
||||
}
|
||||
|
||||
Upstream Host
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
This feature allows you to have ReRoutes based on the upstream host. This works by looking at the host header the client has used and then using this as part of the information we use to identify a ReRoute.
|
||||
|
||||
In order to use this feature please add the following to your config.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"DownstreamPathTemplate": "/",
|
||||
"DownstreamScheme": "https",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "10.0.10.1",
|
||||
"Port": 80,
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"UpstreamHost": "somedomain.com"
|
||||
}
|
||||
|
||||
The ReRoute above will only be matched when the host header value is somedomain.com.
|
||||
|
||||
If you do not set UpstreamHost on a ReRoue then any host header can match it. This is basically a catch all and
|
||||
preservers existing functionality at the time of building the feature. This means that if you have two ReRoutes that are the same apart from the UpstreamHost where one is null and the other set. Ocelot will favour the one that has been set.
|
||||
|
||||
This feature was requested as part of `Issue 216 <https://github.com/TomPallister/Ocelot/pull/216>`_ .
|
||||
|
||||
Priority
|
||||
^^^^^^^^
|
||||
|
||||
In `Issue 270 <https://github.com/TomPallister/Ocelot/pull/270>`_ I finally decided to expose the ReRoute priority in
|
||||
ocelot.json. This means you can decide in what order you want your ReRoutes to match the Upstream HttpRequest.
|
||||
|
||||
In order to get this working add the following to a ReRoute in ocelot.json, 0 is just an example value here but will explain below.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"Priority": 0
|
||||
}
|
||||
|
||||
0 is the lowest priority, Ocelot will always use 0 for /{catchAll} ReRoutes and this is still hardcoded. After that you are free
|
||||
to set any priority you wish.
|
||||
|
||||
e.g. you could have
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"UpstreamPathTemplate": "/goods/{catchAll}"
|
||||
"Priority": 0
|
||||
}
|
||||
|
||||
and
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"UpstreamPathTemplate": "/goods/delete"
|
||||
"Priority": 1
|
||||
}
|
||||
|
||||
In the example above if you make a request into Ocelot on /goods/delete Ocelot will match /goods/delete ReRoute. Previously it would have
|
||||
matched /goods/{catchAll} (because this is the first ReRoute in the list!).
|
||||
|
||||
Dynamic Routing
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
This feature was requested in `issue 340 <https://github.com/TomPallister/Ocelot/issue/340>`_. The idea is to enable dynamic routing
|
||||
when using a service discovery provider so you don't have to provide the ReRoute config. See the docs :ref:`service-discovery` if
|
||||
this sounds interesting to you.
|
||||
|
||||
Query Strings
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
Ocelot allow's you to specify a querystring as part of the DownstreamPathTemplate like the example below.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"ReRoutes": [
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}",
|
||||
"UpstreamPathTemplate": "/api/units/{subscriptionId}/{unitId}/updates",
|
||||
"UpstreamHttpMethod": [
|
||||
"Get"
|
||||
],
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "localhost",
|
||||
"Port": 50110
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"GlobalConfiguration": {
|
||||
"UseServiceDiscovery": false
|
||||
}
|
||||
}
|
||||
|
||||
In this example Ocelot will use the value from the {unitId} in the upstream path template and add it to the downstream request as a query string parameter called unitId!
|
||||
|
||||
Ocelot will also allow you to put query string parametrs in the UpstreamPathTemplate so you can match certain queries to certain services.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"ReRoutes": [
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/units/{subscriptionId}/{unitId}/updates",
|
||||
"UpstreamPathTemplate": "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}",
|
||||
"UpstreamHttpMethod": [
|
||||
"Get"
|
||||
],
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "localhost",
|
||||
"Port": 50110
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"GlobalConfiguration": {
|
||||
"UseServiceDiscovery": false
|
||||
}
|
||||
}
|
||||
|
||||
In this example Ocelot will only match requests that have a matching url path and the querystring starts with unitId=something. You can have other queries after this
|
||||
but you must start with the matching parameter. Also in this example Ocelot will swap the unitId param from the query string and use it in the downstream request path.
|
@ -18,7 +18,8 @@ will be used.
|
||||
|
||||
"ServiceDiscoveryProvider": {
|
||||
"Host": "localhost",
|
||||
"Port": 8500
|
||||
"Port": 8500,
|
||||
"Type": "Consul"
|
||||
}
|
||||
|
||||
In the future we can add a feature that allows ReRoute specfic configuration.
|
||||
@ -136,8 +137,8 @@ The config might look something like
|
||||
"RequestIdKey": null,
|
||||
"ServiceDiscoveryProvider": {
|
||||
"Host": "localhost",
|
||||
"Port": 8510,
|
||||
"Type": null,
|
||||
"Port": 8500,
|
||||
"Type": "Consul",
|
||||
"Token": null,
|
||||
"ConfigurationKey": null
|
||||
},
|
||||
|
@ -4,7 +4,7 @@ Getting Started
|
||||
Ocelot is designed to work with .NET Core only and is currently
|
||||
built to netstandard2.0 `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.
|
||||
|
||||
.NET Core 2.0
|
||||
.NET Core 2.1
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
**Install NuGet package**
|
||||
|
12
global.json
12
global.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"projects": [ "src", "test" ],
|
||||
"sdk": {
|
||||
"version": "2.1.4"
|
||||
}
|
||||
}
|
||||
{
|
||||
"projects": [ "src", "test" ],
|
||||
"sdk": {
|
||||
"version": "2.1.301"
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
private int _durationOfBreak;
|
||||
|
||||
private int _timeoutValue;
|
||||
private int _timeoutValue;
|
||||
|
||||
private string _key;
|
||||
|
||||
|
@ -8,13 +8,16 @@ namespace Ocelot.Configuration.Creator
|
||||
public ServiceProviderConfiguration Create(FileGlobalConfiguration globalConfiguration)
|
||||
{
|
||||
var port = globalConfiguration?.ServiceDiscoveryProvider?.Port ?? 0;
|
||||
var host = globalConfiguration?.ServiceDiscoveryProvider?.Host ?? "consul";
|
||||
var host = globalConfiguration?.ServiceDiscoveryProvider?.Host ?? "localhost";
|
||||
var type = !string.IsNullOrEmpty(globalConfiguration?.ServiceDiscoveryProvider?.Type)
|
||||
? globalConfiguration?.ServiceDiscoveryProvider?.Type
|
||||
: "consul";
|
||||
var pollingInterval = globalConfiguration?.ServiceDiscoveryProvider?.PollingInterval ?? 0;
|
||||
|
||||
return new ServiceProviderConfigurationBuilder()
|
||||
.WithHost(host)
|
||||
.WithPort(port)
|
||||
.WithType(globalConfiguration?.ServiceDiscoveryProvider?.Type)
|
||||
.WithType(type)
|
||||
.WithToken(globalConfiguration?.ServiceDiscoveryProvider?.Token)
|
||||
.WithConfigurationKey(globalConfiguration?.ServiceDiscoveryProvider?.ConfigurationKey)
|
||||
.WithPollingInterval(pollingInterval)
|
||||
|
@ -14,7 +14,8 @@ namespace Ocelot.Configuration.Creator
|
||||
|
||||
public UpstreamPathTemplate Create(IReRoute reRoute)
|
||||
{
|
||||
var upstreamTemplate = reRoute.UpstreamPathTemplate;
|
||||
var upstreamTemplate = reRoute.UpstreamPathTemplate;
|
||||
|
||||
|
||||
var placeholders = new List<string>();
|
||||
|
||||
@ -30,9 +31,17 @@ namespace Ocelot.Configuration.Creator
|
||||
//hack to handle /{url} case
|
||||
if(ForwardSlashAndOnePlaceHolder(upstreamTemplate, placeholders, postitionOfPlaceHolderClosingBracket))
|
||||
{
|
||||
return new UpstreamPathTemplate(RegExForwardSlashAndOnePlaceHolder, 0);
|
||||
return new UpstreamPathTemplate(RegExForwardSlashAndOnePlaceHolder, 0, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var containsQueryString = false;
|
||||
|
||||
if (upstreamTemplate.Contains("?"))
|
||||
{
|
||||
containsQueryString = true;
|
||||
upstreamTemplate = upstreamTemplate.Replace("?", "\\?");
|
||||
}
|
||||
|
||||
foreach (var placeholder in placeholders)
|
||||
@ -42,7 +51,7 @@ namespace Ocelot.Configuration.Creator
|
||||
|
||||
if (upstreamTemplate == "/")
|
||||
{
|
||||
return new UpstreamPathTemplate(RegExForwardSlashOnly, reRoute.Priority);
|
||||
return new UpstreamPathTemplate(RegExForwardSlashOnly, reRoute.Priority, containsQueryString);
|
||||
}
|
||||
|
||||
if(upstreamTemplate.EndsWith("/"))
|
||||
@ -54,7 +63,7 @@ namespace Ocelot.Configuration.Creator
|
||||
? $"^{upstreamTemplate}{RegExMatchEndString}"
|
||||
: $"^{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}";
|
||||
|
||||
return new UpstreamPathTemplate(route, reRoute.Priority);
|
||||
return new UpstreamPathTemplate(route, reRoute.Priority, containsQueryString);
|
||||
}
|
||||
|
||||
private bool ForwardSlashAndOnePlaceHolder(string upstreamTemplate, List<string> placeholders, int postitionOfPlaceHolderClosingBracket)
|
||||
|
@ -29,12 +29,17 @@ namespace Ocelot.DependencyInjection
|
||||
}
|
||||
|
||||
public static IConfigurationBuilder AddOcelot(this IConfigurationBuilder builder)
|
||||
{
|
||||
return builder.AddOcelot(".");
|
||||
}
|
||||
|
||||
public static IConfigurationBuilder AddOcelot(this IConfigurationBuilder builder, string folder)
|
||||
{
|
||||
const string pattern = "(?i)ocelot\\.([a-zA-Z0-9]*)(\\.json)$";
|
||||
|
||||
var reg = new Regex(pattern);
|
||||
|
||||
var files = Directory.GetFiles(".")
|
||||
var files = Directory.GetFiles(folder)
|
||||
.Where(path => reg.IsMatch(path))
|
||||
.ToList();
|
||||
|
||||
@ -43,7 +48,7 @@ namespace Ocelot.DependencyInjection
|
||||
foreach (var file in files)
|
||||
{
|
||||
// windows and unix sigh...
|
||||
if(files.Count > 1 && (file == "./ocelot.json" || file == ".\\ocelot.json"))
|
||||
if(files.Count > 1 && (Path.GetFileName(file) == "ocelot.json"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -53,7 +58,7 @@ namespace Ocelot.DependencyInjection
|
||||
var config = JsonConvert.DeserializeObject<FileConfiguration>(lines);
|
||||
|
||||
// windows and unix sigh...
|
||||
if (file == "./ocelot.global.json" || file == ".\\ocelot.global.json")
|
||||
if (Path.GetFileName(file) == "ocelot.global.json")
|
||||
{
|
||||
fileConfiguration.GlobalConfiguration = config.GlobalConfiguration;
|
||||
}
|
||||
|
@ -4,11 +4,15 @@ using System;
|
||||
using System.Net.Http;
|
||||
using IdentityServer4.AccessTokenValidation;
|
||||
using Ocelot.Middleware.Multiplexer;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Ocelot.DependencyInjection
|
||||
{
|
||||
public interface IOcelotBuilder
|
||||
{
|
||||
IServiceCollection Services { get; }
|
||||
IConfiguration Configuration { get; }
|
||||
IOcelotBuilder AddStoreOcelotConfigurationInConsul();
|
||||
|
||||
IOcelotBuilder AddCacheManager(Action<ConfigurationBuilderCachePart> settings);
|
||||
@ -19,10 +23,7 @@ namespace Ocelot.DependencyInjection
|
||||
|
||||
IOcelotAdministrationBuilder AddAdministration(string path, Action<IdentityServerAuthenticationOptions> configOptions);
|
||||
|
||||
IOcelotBuilder AddSingletonDelegatingHandler<T>(bool global = false)
|
||||
where T : DelegatingHandler;
|
||||
|
||||
IOcelotBuilder AddTransientDelegatingHandler<T>(bool global = false)
|
||||
IOcelotBuilder AddDelegatingHandler<T>(bool global = false)
|
||||
where T : DelegatingHandler;
|
||||
|
||||
IOcelotBuilder AddSingletonDefinedAggregator<T>()
|
||||
|
@ -55,6 +55,10 @@ namespace Ocelot.DependencyInjection
|
||||
private readonly IServiceCollection _services;
|
||||
private readonly IConfiguration _configurationRoot;
|
||||
|
||||
public IServiceCollection Services => _services;
|
||||
|
||||
public IConfiguration Configuration => _configurationRoot;
|
||||
|
||||
public OcelotBuilder(IServiceCollection services, IConfiguration configurationRoot)
|
||||
{
|
||||
_configurationRoot = configurationRoot;
|
||||
@ -212,26 +216,7 @@ namespace Ocelot.DependencyInjection
|
||||
return this;
|
||||
}
|
||||
|
||||
public IOcelotBuilder AddSingletonDelegatingHandler<THandler>(bool global = false)
|
||||
where THandler : DelegatingHandler
|
||||
{
|
||||
if(global)
|
||||
{
|
||||
_services.AddSingleton<THandler>();
|
||||
_services.AddSingleton<GlobalDelegatingHandler>(s => {
|
||||
var service = s.GetService<THandler>();
|
||||
return new GlobalDelegatingHandler(service);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
_services.AddSingleton<DelegatingHandler, THandler>();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public IOcelotBuilder AddTransientDelegatingHandler<THandler>(bool global = false)
|
||||
public IOcelotBuilder AddDelegatingHandler<THandler>(bool global = false)
|
||||
where THandler : DelegatingHandler
|
||||
{
|
||||
if(global)
|
||||
|
@ -21,7 +21,7 @@
|
||||
_cache = new ConcurrentDictionary<string, OkResponse<DownstreamRoute>>();
|
||||
}
|
||||
|
||||
public Response<DownstreamRoute> Get(string upstreamUrlPath, string upstreamHttpMethod, IInternalConfiguration configuration, string upstreamHost)
|
||||
public Response<DownstreamRoute> Get(string upstreamUrlPath, string upstreamQueryString, string upstreamHttpMethod, IInternalConfiguration configuration, string upstreamHost)
|
||||
{
|
||||
var serviceName = GetServiceName(upstreamUrlPath);
|
||||
|
||||
@ -61,8 +61,8 @@
|
||||
|
||||
downstreamRoute = new OkResponse<DownstreamRoute>(new DownstreamRoute(new List<PlaceholderNameAndValue>(), reRoute));
|
||||
|
||||
_cache.AddOrUpdate(loadBalancerKey, downstreamRoute, (x, y) => downstreamRoute);
|
||||
|
||||
_cache.AddOrUpdate(loadBalancerKey, downstreamRoute, (x, y) => downstreamRoute);
|
||||
|
||||
return downstreamRoute;
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ namespace Ocelot.DownstreamRouteFinder.Finder
|
||||
_placeholderNameAndValueFinder = urlPathPlaceholderNameAndValueFinder;
|
||||
}
|
||||
|
||||
public Response<DownstreamRoute> Get(string path, string httpMethod, IInternalConfiguration configuration, string upstreamHost)
|
||||
public Response<DownstreamRoute> Get(string upstreamUrlPath, string upstreamQueryString, string httpMethod, IInternalConfiguration configuration, string upstreamHost)
|
||||
{
|
||||
var downstreamRoutes = new List<DownstreamRoute>();
|
||||
|
||||
@ -28,11 +28,11 @@ namespace Ocelot.DownstreamRouteFinder.Finder
|
||||
|
||||
foreach (var reRoute in applicableReRoutes)
|
||||
{
|
||||
var urlMatch = _urlMatcher.Match(path, reRoute.UpstreamTemplatePattern.Template);
|
||||
var urlMatch = _urlMatcher.Match(upstreamUrlPath, upstreamQueryString, reRoute.UpstreamTemplatePattern.Template, reRoute.UpstreamTemplatePattern.ContainsQueryString);
|
||||
|
||||
if (urlMatch.Data.Match)
|
||||
{
|
||||
downstreamRoutes.Add(GetPlaceholderNamesAndValues(path, reRoute));
|
||||
downstreamRoutes.Add(GetPlaceholderNamesAndValues(upstreamUrlPath, upstreamQueryString, reRoute));
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ namespace Ocelot.DownstreamRouteFinder.Finder
|
||||
return notNullOption != null ? new OkResponse<DownstreamRoute>(notNullOption) : new OkResponse<DownstreamRoute>(nullOption);
|
||||
}
|
||||
|
||||
return new ErrorResponse<DownstreamRoute>(new UnableToFindDownstreamRouteError(path, httpMethod));
|
||||
return new ErrorResponse<DownstreamRoute>(new UnableToFindDownstreamRouteError(upstreamUrlPath, httpMethod));
|
||||
}
|
||||
|
||||
private bool RouteIsApplicableToThisRequest(ReRoute reRoute, string httpMethod, string upstreamHost)
|
||||
@ -53,9 +53,9 @@ namespace Ocelot.DownstreamRouteFinder.Finder
|
||||
(string.IsNullOrEmpty(reRoute.UpstreamHost) || reRoute.UpstreamHost == upstreamHost);
|
||||
}
|
||||
|
||||
private DownstreamRoute GetPlaceholderNamesAndValues(string path, ReRoute reRoute)
|
||||
private DownstreamRoute GetPlaceholderNamesAndValues(string path, string query, ReRoute reRoute)
|
||||
{
|
||||
var templatePlaceholderNameAndValues = _placeholderNameAndValueFinder.Find(path, reRoute.UpstreamPathTemplate.Value);
|
||||
var templatePlaceholderNameAndValues = _placeholderNameAndValueFinder.Find(path, query, reRoute.UpstreamPathTemplate.Value);
|
||||
|
||||
return new DownstreamRoute(templatePlaceholderNameAndValues.Data, reRoute);
|
||||
}
|
||||
|
@ -5,13 +5,16 @@
|
||||
using System.Linq;
|
||||
using Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Ocelot.Logging;
|
||||
|
||||
public class DownstreamRouteProviderFactory : IDownstreamRouteProviderFactory
|
||||
{
|
||||
private readonly Dictionary<string, IDownstreamRouteProvider> _providers;
|
||||
private IOcelotLogger _logger;
|
||||
|
||||
public DownstreamRouteProviderFactory(IServiceProvider provider)
|
||||
public DownstreamRouteProviderFactory(IServiceProvider provider, IOcelotLoggerFactory factory)
|
||||
{
|
||||
_logger = factory.CreateLogger<DownstreamRouteProviderFactory>();
|
||||
_providers = provider.GetServices<IDownstreamRouteProvider>().ToDictionary(x => x.GetType().Name);
|
||||
}
|
||||
|
||||
@ -19,6 +22,7 @@
|
||||
{
|
||||
if(!config.ReRoutes.Any() && IsServiceDiscovery(config.ServiceProviderConfiguration))
|
||||
{
|
||||
_logger.LogInformation($"Selected {nameof(DownstreamRouteCreator)} as DownstreamRouteProvider for this request");
|
||||
return _providers[nameof(DownstreamRouteCreator)];
|
||||
}
|
||||
|
||||
@ -27,7 +31,7 @@
|
||||
|
||||
private bool IsServiceDiscovery(ServiceProviderConfiguration config)
|
||||
{
|
||||
if(!string.IsNullOrEmpty(config?.Host) || config?.Port > 0)
|
||||
if(!string.IsNullOrEmpty(config?.Host) && config?.Port > 0 && !string.IsNullOrEmpty(config.Type))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -6,6 +6,6 @@ namespace Ocelot.DownstreamRouteFinder.Finder
|
||||
{
|
||||
public interface IDownstreamRouteProvider
|
||||
{
|
||||
Response<DownstreamRoute> Get(string upstreamUrlPath, string upstreamHttpMethod, IInternalConfiguration configuration, string upstreamHost);
|
||||
Response<DownstreamRoute> Get(string upstreamUrlPath, string upstreamQueryString, string upstreamHttpMethod, IInternalConfiguration configuration, string upstreamHost);
|
||||
}
|
||||
}
|
||||
|
@ -33,13 +33,15 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
|
||||
{
|
||||
var upstreamUrlPath = context.HttpContext.Request.Path.ToString();
|
||||
|
||||
var upstreamQueryString = context.HttpContext.Request.QueryString.ToString();
|
||||
|
||||
var upstreamHost = context.HttpContext.Request.Headers["Host"];
|
||||
|
||||
Logger.LogDebug($"Upstream url path is {upstreamUrlPath}");
|
||||
|
||||
var provider = _factory.Get(context.Configuration);
|
||||
|
||||
var downstreamRoute = provider.Get(upstreamUrlPath, context.HttpContext.Request.Method, context.Configuration, upstreamHost);
|
||||
var downstreamRoute = provider.Get(upstreamUrlPath, upstreamQueryString, context.HttpContext.Request.Method, context.Configuration, upstreamHost);
|
||||
|
||||
if (downstreamRoute.IsError)
|
||||
{
|
||||
|
@ -5,6 +5,6 @@ namespace Ocelot.DownstreamRouteFinder.UrlMatcher
|
||||
{
|
||||
public interface IPlaceholderNameAndValueFinder
|
||||
{
|
||||
Response<List<PlaceholderNameAndValue>> Find(string path, string pathTemplate);
|
||||
Response<List<PlaceholderNameAndValue>> Find(string path, string query, string pathTemplate);
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,6 @@ namespace Ocelot.DownstreamRouteFinder.UrlMatcher
|
||||
{
|
||||
public interface IUrlPathToUrlTemplateMatcher
|
||||
{
|
||||
Response<UrlMatch> Match(string upstreamUrlPath, string upstreamUrlPathTemplate);
|
||||
Response<UrlMatch> Match(string upstreamUrlPath, string upstreamQueryString, string upstreamUrlPathTemplate, bool containsQueryString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,11 +5,18 @@ namespace Ocelot.DownstreamRouteFinder.UrlMatcher
|
||||
{
|
||||
public class RegExUrlMatcher : IUrlPathToUrlTemplateMatcher
|
||||
{
|
||||
public Response<UrlMatch> Match(string upstreamUrlPath, string upstreamUrlPathTemplate)
|
||||
public Response<UrlMatch> Match(string upstreamUrlPath, string upstreamQueryString, string upstreamUrlPathTemplate, bool containsQueryString)
|
||||
{
|
||||
var regex = new Regex(upstreamUrlPathTemplate);
|
||||
|
||||
return regex.IsMatch(upstreamUrlPath)
|
||||
if (!containsQueryString)
|
||||
{
|
||||
return regex.IsMatch(upstreamUrlPath)
|
||||
? new OkResponse<UrlMatch>(new UrlMatch(true))
|
||||
: new OkResponse<UrlMatch>(new UrlMatch(false));
|
||||
}
|
||||
|
||||
return regex.IsMatch($"{upstreamUrlPath}{upstreamQueryString}")
|
||||
? new OkResponse<UrlMatch>(new UrlMatch(true))
|
||||
: new OkResponse<UrlMatch>(new UrlMatch(false));
|
||||
}
|
||||
|
@ -5,27 +5,46 @@ namespace Ocelot.DownstreamRouteFinder.UrlMatcher
|
||||
{
|
||||
public class UrlPathPlaceholderNameAndValueFinder : IPlaceholderNameAndValueFinder
|
||||
{
|
||||
public Response<List<PlaceholderNameAndValue>> Find(string path, string pathTemplate)
|
||||
public Response<List<PlaceholderNameAndValue>> Find(string path, string query, string pathTemplate)
|
||||
{
|
||||
var placeHolderNameAndValues = new List<PlaceholderNameAndValue>();
|
||||
|
||||
path = $"{path}{query}";
|
||||
|
||||
int counterForPath = 0;
|
||||
|
||||
|
||||
var delimiter = '/';
|
||||
var nextDelimiter = '/';
|
||||
|
||||
for (int counterForTemplate = 0; counterForTemplate < pathTemplate.Length; counterForTemplate++)
|
||||
{
|
||||
if ((path.Length > counterForPath) && CharactersDontMatch(pathTemplate[counterForTemplate], path[counterForPath]) && ContinueScanningUrl(counterForPath,path.Length))
|
||||
{
|
||||
if (IsPlaceholder(pathTemplate[counterForTemplate]))
|
||||
{
|
||||
//should_find_multiple_query_string make test pass
|
||||
if (PassedQueryString(pathTemplate, counterForTemplate))
|
||||
{
|
||||
delimiter = '&';
|
||||
nextDelimiter = '&';
|
||||
}
|
||||
|
||||
//should_find_multiple_query_string_and_path makes test pass
|
||||
if (NotPassedQueryString(pathTemplate, counterForTemplate) && NoMoreForwardSlash(pathTemplate, counterForTemplate))
|
||||
{
|
||||
delimiter = '?';
|
||||
nextDelimiter = '?';
|
||||
}
|
||||
|
||||
var placeholderName = GetPlaceholderName(pathTemplate, counterForTemplate);
|
||||
|
||||
var placeholderValue = GetPlaceholderValue(pathTemplate, placeholderName, path, counterForPath);
|
||||
var placeholderValue = GetPlaceholderValue(pathTemplate, query, placeholderName, path, counterForPath, delimiter);
|
||||
|
||||
placeHolderNameAndValues.Add(new PlaceholderNameAndValue(placeholderName, placeholderValue));
|
||||
|
||||
counterForTemplate = GetNextCounterPosition(pathTemplate, counterForTemplate, '}');
|
||||
|
||||
counterForPath = GetNextCounterPosition(path, counterForPath, '/');
|
||||
counterForPath = GetNextCounterPosition(path, counterForPath, nextDelimiter);
|
||||
|
||||
continue;
|
||||
}
|
||||
@ -44,7 +63,7 @@ namespace Ocelot.DownstreamRouteFinder.UrlMatcher
|
||||
}
|
||||
else
|
||||
{
|
||||
var placeholderValue = GetPlaceholderValue(pathTemplate, placeholderName, path, counterForPath + 1);
|
||||
var placeholderValue = GetPlaceholderValue(pathTemplate, query, placeholderName, path, counterForPath + 1, '/');
|
||||
placeHolderNameAndValues.Add(new PlaceholderNameAndValue(placeholderName, placeholderValue));
|
||||
}
|
||||
|
||||
@ -57,6 +76,21 @@ namespace Ocelot.DownstreamRouteFinder.UrlMatcher
|
||||
return new OkResponse<List<PlaceholderNameAndValue>>(placeHolderNameAndValues);
|
||||
}
|
||||
|
||||
private static bool NoMoreForwardSlash(string pathTemplate, int counterForTemplate)
|
||||
{
|
||||
return !pathTemplate.Substring(counterForTemplate).Contains("/");
|
||||
}
|
||||
|
||||
private static bool NotPassedQueryString(string pathTemplate, int counterForTemplate)
|
||||
{
|
||||
return !pathTemplate.Substring(0, counterForTemplate).Contains("?");
|
||||
}
|
||||
|
||||
private static bool PassedQueryString(string pathTemplate, int counterForTemplate)
|
||||
{
|
||||
return pathTemplate.Substring(0, counterForTemplate).Contains("?");
|
||||
}
|
||||
|
||||
private bool IsCatchAll(string path, int counterForPath, string pathTemplate)
|
||||
{
|
||||
return string.IsNullOrEmpty(path) || (path.Length > counterForPath && path[counterForPath] == '/') && pathTemplate.Length > 1
|
||||
@ -69,11 +103,11 @@ namespace Ocelot.DownstreamRouteFinder.UrlMatcher
|
||||
return path.Length == 1 || path.Length == 0;
|
||||
}
|
||||
|
||||
private string GetPlaceholderValue(string urlPathTemplate, string variableName, string urlPath, int counterForUrl)
|
||||
private string GetPlaceholderValue(string urlPathTemplate, string query, string variableName, string urlPath, int counterForUrl, char delimiter)
|
||||
{
|
||||
var positionOfNextSlash = urlPath.IndexOf('/', counterForUrl);
|
||||
var positionOfNextSlash = urlPath.IndexOf(delimiter, counterForUrl);
|
||||
|
||||
if (positionOfNextSlash == -1 || urlPathTemplate.Trim('/').EndsWith(variableName))
|
||||
if (positionOfNextSlash == -1 || (urlPathTemplate.Trim(delimiter).EndsWith(variableName) && string.IsNullOrEmpty(query)))
|
||||
{
|
||||
positionOfNextSlash = urlPath.Length;
|
||||
}
|
||||
|
@ -1,84 +1,132 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.DownstreamUrlCreator.Middleware
|
||||
{
|
||||
public class DownstreamUrlCreatorMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly IDownstreamPathPlaceholderReplacer _replacer;
|
||||
|
||||
public DownstreamUrlCreatorMiddleware(OcelotRequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IDownstreamPathPlaceholderReplacer replacer)
|
||||
:base(loggerFactory.CreateLogger<DownstreamUrlCreatorMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_replacer = replacer;
|
||||
}
|
||||
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
{
|
||||
var dsPath = _replacer
|
||||
.Replace(context.DownstreamReRoute.DownstreamPathTemplate, context.TemplatePlaceholderNameAndValues);
|
||||
|
||||
if (dsPath.IsError)
|
||||
{
|
||||
Logger.LogDebug("IDownstreamPathPlaceholderReplacer returned an error, setting pipeline error");
|
||||
|
||||
SetPipelineError(context, dsPath.Errors);
|
||||
return;
|
||||
}
|
||||
|
||||
context.DownstreamRequest.Scheme = context.DownstreamReRoute.DownstreamScheme;
|
||||
|
||||
if (ServiceFabricRequest(context))
|
||||
{
|
||||
var pathAndQuery = CreateServiceFabricUri(context, dsPath);
|
||||
context.DownstreamRequest.AbsolutePath = pathAndQuery.path;
|
||||
context.DownstreamRequest.Query = pathAndQuery.query;
|
||||
}
|
||||
else
|
||||
{
|
||||
context.DownstreamRequest.AbsolutePath = dsPath.Data.Value;
|
||||
}
|
||||
|
||||
Logger.LogDebug($"Downstream url is {context.DownstreamRequest}");
|
||||
|
||||
await _next.Invoke(context);
|
||||
}
|
||||
|
||||
private (string path, string query) CreateServiceFabricUri(DownstreamContext context, Response<DownstreamPath> dsPath)
|
||||
{
|
||||
var query = context.DownstreamRequest.Query;
|
||||
var serviceFabricPath = $"/{context.DownstreamReRoute.ServiceName + dsPath.Data.Value}";
|
||||
|
||||
if (RequestForStatefullService(query))
|
||||
{
|
||||
return (serviceFabricPath, query);
|
||||
}
|
||||
|
||||
var split = string.IsNullOrEmpty(query) ? "?" : "&";
|
||||
return (serviceFabricPath, $"{query}{split}cmd=instance");
|
||||
}
|
||||
|
||||
private static bool ServiceFabricRequest(DownstreamContext context)
|
||||
{
|
||||
return context.Configuration.ServiceProviderConfiguration.Type == "ServiceFabric" && context.DownstreamReRoute.UseServiceDiscovery;
|
||||
}
|
||||
|
||||
private static bool RequestForStatefullService(string query)
|
||||
{
|
||||
return query.Contains("PartitionKind") && query.Contains("PartitionKey");
|
||||
}
|
||||
}
|
||||
}
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.DownstreamUrlCreator.Middleware
|
||||
{
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
public class DownstreamUrlCreatorMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly IDownstreamPathPlaceholderReplacer _replacer;
|
||||
|
||||
public DownstreamUrlCreatorMiddleware(OcelotRequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IDownstreamPathPlaceholderReplacer replacer)
|
||||
:base(loggerFactory.CreateLogger<DownstreamUrlCreatorMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_replacer = replacer;
|
||||
}
|
||||
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
{
|
||||
var response = _replacer
|
||||
.Replace(context.DownstreamReRoute.DownstreamPathTemplate, context.TemplatePlaceholderNameAndValues);
|
||||
|
||||
if (response.IsError)
|
||||
{
|
||||
Logger.LogDebug("IDownstreamPathPlaceholderReplacer returned an error, setting pipeline error");
|
||||
|
||||
SetPipelineError(context, response.Errors);
|
||||
return;
|
||||
}
|
||||
|
||||
context.DownstreamRequest.Scheme = context.DownstreamReRoute.DownstreamScheme;
|
||||
|
||||
if (ServiceFabricRequest(context))
|
||||
{
|
||||
var pathAndQuery = CreateServiceFabricUri(context, response);
|
||||
context.DownstreamRequest.AbsolutePath = pathAndQuery.path;
|
||||
context.DownstreamRequest.Query = pathAndQuery.query;
|
||||
}
|
||||
else
|
||||
{
|
||||
var dsPath = response.Data;
|
||||
|
||||
if(ContainsQueryString(dsPath))
|
||||
{
|
||||
context.DownstreamRequest.AbsolutePath = GetPath(dsPath);
|
||||
context.DownstreamRequest.Query = GetQueryString(dsPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveQueryStringParametersThatHaveBeenUsedInTemplate(context);
|
||||
|
||||
context.DownstreamRequest.AbsolutePath = dsPath.Value;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.LogDebug($"Downstream url is {context.DownstreamRequest}");
|
||||
|
||||
await _next.Invoke(context);
|
||||
}
|
||||
|
||||
private static void RemoveQueryStringParametersThatHaveBeenUsedInTemplate(DownstreamContext context)
|
||||
{
|
||||
foreach (var nAndV in context.TemplatePlaceholderNameAndValues)
|
||||
{
|
||||
var name = nAndV.Name.Replace("{", "").Replace("}", "");
|
||||
|
||||
if (context.DownstreamRequest.Query.Contains(name) &&
|
||||
context.DownstreamRequest.Query.Contains(nAndV.Value))
|
||||
{
|
||||
var questionMarkOrAmpersand = context.DownstreamRequest.Query.IndexOf(name, StringComparison.Ordinal);
|
||||
context.DownstreamRequest.Query = context.DownstreamRequest.Query.Remove(questionMarkOrAmpersand - 1, 1);
|
||||
|
||||
var rgx = new Regex($@"\b{name}={nAndV.Value}\b");
|
||||
context.DownstreamRequest.Query = rgx.Replace(context.DownstreamRequest.Query, "");
|
||||
|
||||
if (!string.IsNullOrEmpty(context.DownstreamRequest.Query))
|
||||
{
|
||||
context.DownstreamRequest.Query = '?' + context.DownstreamRequest.Query.Substring(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetPath(DownstreamPath dsPath)
|
||||
{
|
||||
return dsPath.Value.Substring(0, dsPath.Value.IndexOf("?", StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
private string GetQueryString(DownstreamPath dsPath)
|
||||
{
|
||||
return dsPath.Value.Substring(dsPath.Value.IndexOf("?", StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
private bool ContainsQueryString(DownstreamPath dsPath)
|
||||
{
|
||||
return dsPath.Value.Contains("?");
|
||||
}
|
||||
|
||||
private (string path, string query) CreateServiceFabricUri(DownstreamContext context, Response<DownstreamPath> dsPath)
|
||||
{
|
||||
var query = context.DownstreamRequest.Query;
|
||||
var serviceFabricPath = $"/{context.DownstreamReRoute.ServiceName + dsPath.Data.Value}";
|
||||
|
||||
if (RequestForStatefullService(query))
|
||||
{
|
||||
return (serviceFabricPath, query);
|
||||
}
|
||||
|
||||
var split = string.IsNullOrEmpty(query) ? "?" : "&";
|
||||
return (serviceFabricPath, $"{query}{split}cmd=instance");
|
||||
}
|
||||
|
||||
private static bool ServiceFabricRequest(DownstreamContext context)
|
||||
{
|
||||
return context.Configuration.ServiceProviderConfiguration.Type == "ServiceFabric" && context.DownstreamReRoute.UseServiceDiscovery;
|
||||
}
|
||||
|
||||
private static bool RequestForStatefullService(string query)
|
||||
{
|
||||
return query.Contains("PartitionKind") && query.Contains("PartitionKey");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ namespace Ocelot.Errors.Middleware
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
{
|
||||
//try and get the global request id and set it for logs...
|
||||
//should this basically be immutable per request...i guess it should!
|
||||
//first thing is get config
|
||||
@ -44,7 +44,7 @@ namespace Ocelot.Errors.Middleware
|
||||
{
|
||||
throw new Exception($"{MiddlewareName} setting pipeline errors. IOcelotConfigurationProvider returned {configuration.Errors.ToErrorString()}");
|
||||
}
|
||||
|
||||
|
||||
TrySetGlobalRequestId(context, configuration.Data);
|
||||
|
||||
context.Configuration = configuration.Data;
|
||||
|
@ -38,6 +38,11 @@ namespace Ocelot.Headers.Middleware
|
||||
|
||||
await _next.Invoke(context);
|
||||
|
||||
if(context.IsError)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var postFAndRs = context.DownstreamReRoute.DownstreamHeadersFindAndReplace;
|
||||
|
||||
_postReplacer.Replace(context.DownstreamResponse, postFAndRs, context.DownstreamRequest);
|
||||
|
@ -20,7 +20,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext downstreamContext)
|
||||
{
|
||||
var services = await _services();
|
||||
//todo first or default could be null..
|
||||
|
||||
if (services == null || services.Count == 0)
|
||||
{
|
||||
return new ErrorResponse<ServiceHostAndPort>(new ServicesAreEmptyError("There were no services in NoLoadBalancer"));
|
||||
|
@ -27,15 +27,21 @@
|
||||
await builder.UseOcelot(new OcelotPipelineConfiguration());
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder, Action<OcelotPipelineConfiguration> pipelineConfiguration)
|
||||
{
|
||||
var config = new OcelotPipelineConfiguration();
|
||||
pipelineConfiguration?.Invoke(config);
|
||||
return await builder.UseOcelot(config);
|
||||
}
|
||||
public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration)
|
||||
{
|
||||
var configuration = await CreateConfiguration(builder);
|
||||
|
||||
var configuration = await CreateConfiguration(builder);
|
||||
|
||||
CreateAdministrationArea(builder, configuration);
|
||||
|
||||
if(UsingRafty(builder))
|
||||
if (UsingRafty(builder))
|
||||
{
|
||||
SetUpRafty(builder);
|
||||
}
|
||||
@ -78,7 +84,7 @@
|
||||
private static bool UsingRafty(IApplicationBuilder builder)
|
||||
{
|
||||
var possible = builder.ApplicationServices.GetService(typeof(INode)) as INode;
|
||||
if(possible != null)
|
||||
if (possible != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -99,9 +105,9 @@
|
||||
{
|
||||
// make configuration from file system?
|
||||
// earlier user needed to add ocelot files in startup configuration stuff, asp.net will map it to this
|
||||
var fileConfig = (IOptions<FileConfiguration>)builder.ApplicationServices.GetService(typeof(IOptions<FileConfiguration>));
|
||||
|
||||
// now create the config
|
||||
var fileConfig = (IOptions<FileConfiguration>)builder.ApplicationServices.GetService(typeof(IOptions<FileConfiguration>));
|
||||
|
||||
// now create the config
|
||||
var internalConfigCreator = (IInternalConfigurationCreator)builder.ApplicationServices.GetService(typeof(IInternalConfigurationCreator));
|
||||
var internalConfig = await internalConfigCreator.Create(fileConfig.Value);
|
||||
|
||||
@ -196,7 +202,7 @@
|
||||
{
|
||||
var ocelotConfiguration = provider.Get();
|
||||
|
||||
if(ocelotConfiguration?.Data == null || ocelotConfiguration.IsError)
|
||||
if (ocelotConfiguration?.Data == null || ocelotConfiguration.IsError)
|
||||
{
|
||||
ThrowToStopOcelotStarting(ocelotConfiguration);
|
||||
}
|
||||
@ -216,7 +222,7 @@
|
||||
|
||||
private static void CreateAdministrationArea(IApplicationBuilder builder, IInternalConfiguration configuration)
|
||||
{
|
||||
if(!string.IsNullOrEmpty(configuration.AdministrationPath))
|
||||
if (!string.IsNullOrEmpty(configuration.AdministrationPath))
|
||||
{
|
||||
builder.Map(configuration.AdministrationPath, app =>
|
||||
{
|
||||
@ -239,8 +245,8 @@
|
||||
var listener = (OcelotDiagnosticListener)builder.ApplicationServices.GetService(typeof(OcelotDiagnosticListener));
|
||||
var diagnosticListener = (DiagnosticListener)builder.ApplicationServices.GetService(typeof(DiagnosticListener));
|
||||
diagnosticListener.SubscribeWithAdapter(listener);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void OnShutdown(IApplicationBuilder app)
|
||||
{
|
||||
var node = (INode)app.ApplicationServices.GetService(typeof(INode));
|
||||
|
@ -1,6 +1,8 @@
|
||||
namespace Ocelot.Middleware
|
||||
{
|
||||
using Ocelot.Middleware.Pipeline;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class OcelotPipelineConfiguration
|
||||
@ -37,6 +39,10 @@
|
||||
/// <summary>
|
||||
/// This allows the user to implement there own query string manipulation logic
|
||||
/// </summary>
|
||||
public Func<DownstreamContext, Func<Task>, Task> PreQueryStringBuilderMiddleware { get; set; }
|
||||
public Func<DownstreamContext, Func<Task>, Task> PreQueryStringBuilderMiddleware { get; set; }
|
||||
/// <summary>
|
||||
/// This is an extension that will branch to different pipes
|
||||
/// </summary>
|
||||
public List<Func<IOcelotPipelineBuilder, Func<DownstreamContext, bool>>> MapWhenOcelotPipeline { get; } = new List<Func<IOcelotPipelineBuilder, Func<DownstreamContext, bool>>>();
|
||||
}
|
||||
}
|
||||
|
@ -80,13 +80,14 @@ namespace Ocelot.Middleware.Pipeline
|
||||
var diagnosticListener = (DiagnosticListener)app.ApplicationServices.GetService(typeof(DiagnosticListener));
|
||||
var middlewareName = ocelotDelegate.Target.GetType().Name;
|
||||
|
||||
OcelotRequestDelegate wrapped = context => {
|
||||
OcelotRequestDelegate wrapped = context =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Write(diagnosticListener, "Ocelot.MiddlewareStarted", middlewareName, context);
|
||||
return ocelotDelegate(context);
|
||||
}
|
||||
catch(Exception ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteException(diagnosticListener, ex, "Ocelot.MiddlewareException", middlewareName, context);
|
||||
throw ex;
|
||||
@ -117,7 +118,7 @@ namespace Ocelot.Middleware.Pipeline
|
||||
|
||||
private static void Write(DiagnosticListener diagnosticListener, string message, string middlewareName, DownstreamContext context)
|
||||
{
|
||||
if(diagnosticListener != null)
|
||||
if (diagnosticListener != null)
|
||||
{
|
||||
diagnosticListener.Write(message, new { name = middlewareName, context = context });
|
||||
}
|
||||
@ -125,7 +126,7 @@ namespace Ocelot.Middleware.Pipeline
|
||||
|
||||
private static void WriteException(DiagnosticListener diagnosticListener, Exception exception, string message, string middlewareName, DownstreamContext context)
|
||||
{
|
||||
if(diagnosticListener != null)
|
||||
if (diagnosticListener != null)
|
||||
{
|
||||
diagnosticListener.Write(message, new { name = middlewareName, context = context, exception = exception });
|
||||
}
|
||||
@ -160,6 +161,28 @@ namespace Ocelot.Middleware.Pipeline
|
||||
return app.Use(next => new MapWhenMiddleware(next, options).Invoke);
|
||||
}
|
||||
|
||||
public static IOcelotPipelineBuilder MapWhen(this IOcelotPipelineBuilder app, Func<IOcelotPipelineBuilder, Predicate> pipelineBuilderFunc)
|
||||
{
|
||||
if (app == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(app));
|
||||
}
|
||||
if (pipelineBuilderFunc == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(pipelineBuilderFunc));
|
||||
}
|
||||
var branchBuilder = app.New();
|
||||
var predicate = pipelineBuilderFunc.Invoke(app);
|
||||
var branch = branchBuilder.Build();
|
||||
|
||||
var options = new MapWhenOptions
|
||||
{
|
||||
Predicate = predicate,
|
||||
Branch = branch
|
||||
};
|
||||
return app.Use(next => new MapWhenMiddleware(next, options).Invoke);
|
||||
}
|
||||
|
||||
private static Func<T, DownstreamContext, IServiceProvider, Task> Compile<T>(MethodInfo methodinfo, ParameterInfo[] parameters)
|
||||
{
|
||||
var middleware = typeof(T);
|
||||
|
@ -28,6 +28,15 @@ namespace Ocelot.Middleware.Pipeline
|
||||
// It also sets the Request Id if anything is set globally
|
||||
builder.UseExceptionHandlerMiddleware();
|
||||
|
||||
//Expand other branch pipes
|
||||
if (pipelineConfiguration.MapWhenOcelotPipeline != null)
|
||||
{
|
||||
foreach (var pipeline in pipelineConfiguration.MapWhenOcelotPipeline)
|
||||
{
|
||||
builder.MapWhen(pipeline);
|
||||
}
|
||||
}
|
||||
|
||||
// If the request is for websockets upgrade we fork into a different pipeline
|
||||
builder.MapWhen(context => context.HttpContext.WebSockets.IsWebSocketRequest,
|
||||
app =>
|
||||
|
@ -25,37 +25,37 @@
|
||||
<DebugSymbols>True</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Butterfly.Client" Version="0.0.8"/>
|
||||
<PackageReference Include="Butterfly.Client" Version="0.0.8" />
|
||||
<PackageReference Include="Butterfly.Client.AspNetCore" Version="0.0.8">
|
||||
<NoWarn>NU1701</NoWarn>
|
||||
</PackageReference>
|
||||
<PackageReference Include="FluentValidation" Version="7.5.2"/>
|
||||
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="2.6.0"/>
|
||||
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.3"/>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.4"/>
|
||||
<PackageReference Include="Microsoft.AspNetCore.MiddlewareAnalysis" Version="2.0.3"/>
|
||||
<PackageReference Include="Microsoft.Data.SQLite" Version="2.0.1"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.2"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.2"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.2"/>
|
||||
<PackageReference Include="Microsoft.Extensions.DiagnosticAdapter" Version="2.0.1">
|
||||
<PackageReference Include="FluentValidation" Version="7.6.104" />
|
||||
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="2.6.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.MiddlewareAnalysis" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Data.SQLite" Version="2.1.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.DiagnosticAdapter" Version="2.1.0">
|
||||
<NoWarn>NU1701</NoWarn>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.2"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.2"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.2"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.2"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.1.1" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.0"/>
|
||||
<PackageReference Include="CacheManager.Core" Version="1.1.2"/>
|
||||
<PackageReference Include="CacheManager.Microsoft.Extensions.Configuration" Version="1.1.2"/>
|
||||
<PackageReference Include="CacheManager.Microsoft.Extensions.Logging" Version="1.1.2"/>
|
||||
<PackageReference Include="Consul" Version="0.7.2.4"/>
|
||||
<PackageReference Include="Polly" Version="6.0.1"/>
|
||||
<PackageReference Include="IdentityServer4" Version="2.2.0"/>
|
||||
<PackageReference Include="Pivotal.Discovery.ClientCore" Version="2.0.1"/>
|
||||
<PackageReference Include="Rafty" Version="0.4.4"/>
|
||||
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.0" />
|
||||
<PackageReference Include="CacheManager.Core" Version="1.1.2" />
|
||||
<PackageReference Include="CacheManager.Microsoft.Extensions.Configuration" Version="1.1.2" />
|
||||
<PackageReference Include="CacheManager.Microsoft.Extensions.Logging" Version="1.1.2" />
|
||||
<PackageReference Include="Consul" Version="0.7.2.5" />
|
||||
<PackageReference Include="Polly" Version="6.0.1" />
|
||||
<PackageReference Include="IdentityServer4" Version="2.2.0" />
|
||||
<PackageReference Include="Pivotal.Discovery.ClientCore" Version="2.0.1" />
|
||||
<PackageReference Include="Rafty" Version="0.4.4" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -99,6 +99,7 @@ namespace Ocelot.Raft
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_sempaphore.Release();
|
||||
return result;
|
||||
}
|
||||
@ -120,6 +121,7 @@ namespace Ocelot.Raft
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_sempaphore.Release();
|
||||
return result;
|
||||
}
|
||||
@ -135,6 +137,7 @@ namespace Ocelot.Raft
|
||||
TypeNameHandling = TypeNameHandling.All
|
||||
};
|
||||
var data = JsonConvert.SerializeObject(log, jsonSerializerSettings);
|
||||
|
||||
//todo - sql injection dont copy this..
|
||||
var sql = $"insert into logs (data) values ('{data}')";
|
||||
_logger.LogInformation($"id: {_nodeId.Id}, sql: {sql}");
|
||||
@ -162,6 +165,7 @@ namespace Ocelot.Raft
|
||||
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
||||
{
|
||||
connection.Open();
|
||||
|
||||
//todo - sql injection dont copy this..
|
||||
var sql = $"select data from logs where id = {index};";
|
||||
_logger.LogInformation($"id: {_nodeId.Id} sql: {sql}");
|
||||
@ -188,6 +192,7 @@ namespace Ocelot.Raft
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_sempaphore.Release();
|
||||
}
|
||||
|
||||
@ -197,6 +202,7 @@ namespace Ocelot.Raft
|
||||
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
||||
{
|
||||
connection.Open();
|
||||
|
||||
//todo - sql injection dont copy this..
|
||||
var sql = $"select data from logs where id = {index};";
|
||||
using (var command = new SqliteCommand(sql, connection))
|
||||
@ -227,6 +233,7 @@ namespace Ocelot.Raft
|
||||
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
||||
{
|
||||
connection.Open();
|
||||
|
||||
//todo - sql injection dont copy this..
|
||||
var sql = $"select data from logs where id = {index}";
|
||||
using (var command = new SqliteCommand(sql, connection))
|
||||
@ -251,6 +258,7 @@ namespace Ocelot.Raft
|
||||
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
||||
{
|
||||
connection.Open();
|
||||
|
||||
//todo - sql injection dont copy this..
|
||||
var sql = $"select id, data from logs where id >= {index}";
|
||||
using (var command = new SqliteCommand(sql, connection))
|
||||
@ -267,10 +275,10 @@ namespace Ocelot.Raft
|
||||
};
|
||||
var log = JsonConvert.DeserializeObject<LogEntry>(data, jsonSerializerSettings);
|
||||
logsToReturn.Add((id, log));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_sempaphore.Release();
|
||||
return logsToReturn;
|
||||
}
|
||||
@ -283,6 +291,7 @@ namespace Ocelot.Raft
|
||||
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
||||
{
|
||||
connection.Open();
|
||||
|
||||
//todo - sql injection dont copy this..
|
||||
var sql = $"select data from logs where id = {index}";
|
||||
using (var command = new SqliteCommand(sql, connection))
|
||||
@ -299,15 +308,18 @@ namespace Ocelot.Raft
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_sempaphore.Release();
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task Remove(int indexOfCommand)
|
||||
{
|
||||
_sempaphore.Wait();
|
||||
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
||||
{
|
||||
connection.Open();
|
||||
|
||||
//todo - sql injection dont copy this..
|
||||
var deleteSql = $"delete from logs where id >= {indexOfCommand};";
|
||||
_logger.LogInformation($"id: {_nodeId.Id} Remove {deleteSql}");
|
||||
@ -316,6 +328,7 @@ namespace Ocelot.Raft
|
||||
var result = await deleteCommand.ExecuteNonQueryAsync();
|
||||
}
|
||||
}
|
||||
|
||||
_sempaphore.Release();
|
||||
}
|
||||
}
|
||||
|
@ -2,13 +2,12 @@ namespace Ocelot.Request.Creator
|
||||
{
|
||||
using System.Net.Http;
|
||||
using Ocelot.Request.Middleware;
|
||||
using System.Runtime.InteropServices;
|
||||
using Ocelot.Infrastructure;
|
||||
|
||||
public class DownstreamRequestCreator : IDownstreamRequestCreator
|
||||
{
|
||||
private readonly IFrameworkDescription _framework;
|
||||
private const string dotNetFramework = ".NET Framework";
|
||||
private const string DotNetFramework = ".NET Framework";
|
||||
|
||||
public DownstreamRequestCreator(IFrameworkDescription framework)
|
||||
{
|
||||
@ -24,8 +23,7 @@ namespace Ocelot.Request.Creator
|
||||
* And MS HttpClient in Full Framework actually rejects it.
|
||||
* see #366 issue
|
||||
**/
|
||||
|
||||
if(_framework.Get().Contains(dotNetFramework))
|
||||
if(_framework.Get().Contains(DotNetFramework))
|
||||
{
|
||||
if (request.Method == HttpMethod.Get ||
|
||||
request.Method == HttpMethod.Head ||
|
||||
|
@ -28,6 +28,7 @@ namespace Ocelot.Request.Middleware
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
{
|
||||
var downstreamRequest = await _requestMapper.Map(context.HttpContext.Request);
|
||||
|
||||
if (downstreamRequest.IsError)
|
||||
{
|
||||
SetPipelineError(context, downstreamRequest.Errors);
|
||||
|
@ -40,6 +40,7 @@ namespace Ocelot.Requester
|
||||
|
||||
if (httpClient != null)
|
||||
{
|
||||
_client = httpClient;
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
|
@ -1,16 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
using System;
|
||||
|
||||
public interface IHttpClientCache
|
||||
{
|
||||
bool Exists(string id);
|
||||
IHttpClient Get(string id);
|
||||
void Remove(string id);
|
||||
void Set(string id, IHttpClient handler, TimeSpan expirationTime);
|
||||
IHttpClient Get(string key);
|
||||
void Set(string key, IHttpClient handler, TimeSpan expirationTime);
|
||||
}
|
||||
}
|
||||
|
@ -1,54 +1,26 @@
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
public class MemoryHttpClientCache : IHttpClientCache
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, ConcurrentQueue<IHttpClient>> _httpClientsCache = new ConcurrentDictionary<string, ConcurrentQueue<IHttpClient>>();
|
||||
|
||||
public void Set(string id, IHttpClient client, TimeSpan expirationTime)
|
||||
{
|
||||
ConcurrentQueue<IHttpClient> connectionQueue;
|
||||
if (_httpClientsCache.TryGetValue(id, out connectionQueue))
|
||||
{
|
||||
connectionQueue.Enqueue(client);
|
||||
}
|
||||
else
|
||||
{
|
||||
connectionQueue = new ConcurrentQueue<IHttpClient>();
|
||||
connectionQueue.Enqueue(client);
|
||||
_httpClientsCache.TryAdd(id, connectionQueue);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Exists(string id)
|
||||
{
|
||||
ConcurrentQueue<IHttpClient> connectionQueue;
|
||||
return _httpClientsCache.TryGetValue(id, out connectionQueue);
|
||||
}
|
||||
|
||||
public IHttpClient Get(string id)
|
||||
{
|
||||
IHttpClient client= null;
|
||||
ConcurrentQueue<IHttpClient> connectionQueue;
|
||||
if (_httpClientsCache.TryGetValue(id, out connectionQueue))
|
||||
{
|
||||
connectionQueue.TryDequeue(out client);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
public void Remove(string id)
|
||||
{
|
||||
ConcurrentQueue<IHttpClient> connectionQueue;
|
||||
_httpClientsCache.TryRemove(id, out connectionQueue);
|
||||
}
|
||||
}
|
||||
}
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
public class MemoryHttpClientCache : IHttpClientCache
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, IHttpClient> _httpClientsCache;
|
||||
|
||||
public MemoryHttpClientCache()
|
||||
{
|
||||
_httpClientsCache = new ConcurrentDictionary<string, IHttpClient>();
|
||||
}
|
||||
|
||||
public void Set(string key, IHttpClient client, TimeSpan expirationTime)
|
||||
{
|
||||
_httpClientsCache.AddOrUpdate(key, client, (k, oldValue) => client);
|
||||
}
|
||||
|
||||
public IHttpClient Get(string key)
|
||||
{
|
||||
//todo handle error?
|
||||
return _httpClientsCache.TryGetValue(key, out var client) ? client : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,14 +2,16 @@ namespace Ocelot.Values
|
||||
{
|
||||
public class UpstreamPathTemplate
|
||||
{
|
||||
public UpstreamPathTemplate(string template, int priority)
|
||||
public UpstreamPathTemplate(string template, int priority, bool containsQueryString)
|
||||
{
|
||||
Template = template;
|
||||
Priority = priority;
|
||||
ContainsQueryString = containsQueryString;
|
||||
}
|
||||
|
||||
public string Template { get; }
|
||||
|
||||
public int Priority { get; }
|
||||
public int Priority { get; }
|
||||
public bool ContainsQueryString { get; }
|
||||
}
|
||||
}
|
||||
|
@ -101,6 +101,57 @@ namespace Ocelot.AcceptanceTests
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_call_global_di_handlers_multiple_times()
|
||||
{
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/",
|
||||
DownstreamScheme = "http",
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||
{
|
||||
new FileHostAndPort
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 9187,
|
||||
}
|
||||
},
|
||||
UpstreamPathTemplate = "/",
|
||||
UpstreamHttpMethod = new List<string> { "Get" },
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:9187", "/", 200, "Hello from Laura"))
|
||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||
.And(x => _steps.GivenOcelotIsRunningWithGlobalHandlerRegisteredInDi<FakeHandlerAgain>())
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_call_global_di_handlers_with_dependency()
|
||||
{
|
||||
@ -198,6 +249,17 @@ namespace Ocelot.AcceptanceTests
|
||||
return base.SendAsync(request, cancellationToken);
|
||||
}
|
||||
}
|
||||
// ReSharper disable once ClassNeverInstantiated.Local
|
||||
private class FakeHandlerAgain : DelegatingHandler
|
||||
{
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
Console.WriteLine(request.RequestUri);
|
||||
|
||||
//do stuff and optionally call the base handler..
|
||||
return await base.SendAsync(request, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
|
||||
{
|
||||
|
@ -1,8 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<VersionPrefix>0.0.0-dev</VersionPrefix>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<RuntimeFrameworkVersion>2.0.0</RuntimeFrameworkVersion>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<AssemblyName>Ocelot.AcceptanceTests</AssemblyName>
|
||||
<OutputType>Exe</OutputType>
|
||||
<PackageId>Ocelot.AcceptanceTests</PackageId>
|
||||
@ -34,25 +33,25 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CacheManager.Serialization.Json" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.8" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.0.3" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.2" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.DotNet.InternalAbstractions" Version="1.0.500-preview2-1-003177" />
|
||||
<PackageReference Include="Shouldly" Version="3.0.0" />
|
||||
<PackageReference Include="TestStack.BDDfy" Version="4.3.2" />
|
||||
<PackageReference Include="Consul" Version="0.7.2.4" />
|
||||
<PackageReference Include="Consul" Version="0.7.2.5" />
|
||||
<PackageReference Include="xunit" Version="2.3.1" />
|
||||
<PackageReference Include="Butterfly.Client.AspNetCore" Version="0.0.8" />
|
||||
<PackageReference Include="Rafty" Version="0.4.4" />
|
||||
|
84
test/Ocelot.AcceptanceTests/ResponseCodeTests.cs
Normal file
84
test/Ocelot.AcceptanceTests/ResponseCodeTests.cs
Normal file
@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Configuration.File;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
public class ResponseCodeTests : IDisposable
|
||||
{
|
||||
private IWebHost _builder;
|
||||
private readonly Steps _steps;
|
||||
private string _downstreamPath;
|
||||
|
||||
public ResponseCodeTests()
|
||||
{
|
||||
_steps = new Steps();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_response_304_when_service_returns_304()
|
||||
{
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/{everything}",
|
||||
DownstreamScheme = "http",
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||
{
|
||||
new FileHostAndPort
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 51879,
|
||||
}
|
||||
},
|
||||
UpstreamPathTemplate = "/{everything}",
|
||||
UpstreamHttpMethod = new List<string> { "Get" },
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/inline.132.bundle.js", 304))
|
||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||
.And(x => _steps.GivenOcelotIsRunning())
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/inline.132.bundle.js"))
|
||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotModified))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode)
|
||||
{
|
||||
_builder = new WebHostBuilder()
|
||||
.UseUrls(baseUrl)
|
||||
.UseKestrel()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseIISIntegration()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UsePathBase(basePath);
|
||||
app.Run(async context =>
|
||||
{
|
||||
context.Response.StatusCode = statusCode;
|
||||
});
|
||||
})
|
||||
.Build();
|
||||
|
||||
_builder.Start();
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
_builder?.Dispose();
|
||||
_steps.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
@ -984,7 +984,7 @@ namespace Ocelot.AcceptanceTests
|
||||
app.Run(async context =>
|
||||
{
|
||||
_downstreamPath = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
|
||||
|
||||
|
||||
if(_downstreamPath != basePath)
|
||||
{
|
||||
context.Response.StatusCode = statusCode;
|
||||
|
249
test/Ocelot.AcceptanceTests/RoutingWithQueryStringTests.cs
Normal file
249
test/Ocelot.AcceptanceTests/RoutingWithQueryStringTests.cs
Normal file
@ -0,0 +1,249 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Configuration.File;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
public class RoutingWithQueryStringTests : IDisposable
|
||||
{
|
||||
private IWebHost _builder;
|
||||
private readonly Steps _steps;
|
||||
private string _downstreamPath;
|
||||
|
||||
public RoutingWithQueryStringTests()
|
||||
{
|
||||
_steps = new Steps();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_response_200_with_query_string_template()
|
||||
{
|
||||
var subscriptionId = Guid.NewGuid().ToString();
|
||||
var unitId = Guid.NewGuid().ToString();
|
||||
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}",
|
||||
DownstreamScheme = "http",
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||
{
|
||||
new FileHostAndPort
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 61879,
|
||||
}
|
||||
},
|
||||
UpstreamPathTemplate = "/api/units/{subscriptionId}/{unitId}/updates",
|
||||
UpstreamHttpMethod = new List<string> { "Get" },
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:61879", $"/api/subscriptions/{subscriptionId}/updates", $"?unitId={unitId}", 200, "Hello from Laura"))
|
||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||
.And(x => _steps.GivenOcelotIsRunning())
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway($"/api/units/{subscriptionId}/{unitId}/updates"))
|
||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_response_200_with_query_string_upstream_template()
|
||||
{
|
||||
var subscriptionId = Guid.NewGuid().ToString();
|
||||
var unitId = Guid.NewGuid().ToString();
|
||||
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/api/units/{subscriptionId}/{unitId}/updates",
|
||||
DownstreamScheme = "http",
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||
{
|
||||
new FileHostAndPort
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 64879,
|
||||
}
|
||||
},
|
||||
UpstreamPathTemplate = "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}",
|
||||
UpstreamHttpMethod = new List<string> { "Get" },
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:64879", $"/api/units/{subscriptionId}/{unitId}/updates", "", 200, "Hello from Laura"))
|
||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||
.And(x => _steps.GivenOcelotIsRunning())
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway($"/api/subscriptions/{subscriptionId}/updates?unitId={unitId}"))
|
||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_response_404_with_query_string_upstream_template_no_query_string()
|
||||
{
|
||||
var subscriptionId = Guid.NewGuid().ToString();
|
||||
var unitId = Guid.NewGuid().ToString();
|
||||
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/api/units/{subscriptionId}/{unitId}/updates",
|
||||
DownstreamScheme = "http",
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||
{
|
||||
new FileHostAndPort
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 64879,
|
||||
}
|
||||
},
|
||||
UpstreamPathTemplate = "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}",
|
||||
UpstreamHttpMethod = new List<string> { "Get" },
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:64879", $"/api/units/{subscriptionId}/{unitId}/updates", "", 200, "Hello from Laura"))
|
||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||
.And(x => _steps.GivenOcelotIsRunning())
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway($"/api/subscriptions/{subscriptionId}/updates"))
|
||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_response_404_with_query_string_upstream_template_different_query_string()
|
||||
{
|
||||
var subscriptionId = Guid.NewGuid().ToString();
|
||||
var unitId = Guid.NewGuid().ToString();
|
||||
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/api/units/{subscriptionId}/{unitId}/updates",
|
||||
DownstreamScheme = "http",
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||
{
|
||||
new FileHostAndPort
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 64879,
|
||||
}
|
||||
},
|
||||
UpstreamPathTemplate = "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}",
|
||||
UpstreamHttpMethod = new List<string> { "Get" },
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:64879", $"/api/units/{subscriptionId}/{unitId}/updates", "", 200, "Hello from Laura"))
|
||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||
.And(x => _steps.GivenOcelotIsRunning())
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway($"/api/subscriptions/{subscriptionId}/updates?test=1"))
|
||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_response_200_with_query_string_upstream_template_multiple_params()
|
||||
{
|
||||
var subscriptionId = Guid.NewGuid().ToString();
|
||||
var unitId = Guid.NewGuid().ToString();
|
||||
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/api/units/{subscriptionId}/{unitId}/updates",
|
||||
DownstreamScheme = "http",
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||
{
|
||||
new FileHostAndPort
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 64879,
|
||||
}
|
||||
},
|
||||
UpstreamPathTemplate = "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}",
|
||||
UpstreamHttpMethod = new List<string> { "Get" },
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:64879", $"/api/units/{subscriptionId}/{unitId}/updates", "?productId=1", 200, "Hello from Laura"))
|
||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||
.And(x => _steps.GivenOcelotIsRunning())
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway($"/api/subscriptions/{subscriptionId}/updates?unitId={unitId}&productId=1"))
|
||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, string queryString, int statusCode, string responseBody)
|
||||
{
|
||||
_builder = new WebHostBuilder()
|
||||
.UseUrls(baseUrl)
|
||||
.UseKestrel()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseIISIntegration()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UsePathBase(basePath);
|
||||
app.Run(async context =>
|
||||
{
|
||||
if(context.Request.PathBase.Value != basePath || context.Request.QueryString.Value != queryString)
|
||||
{
|
||||
context.Response.StatusCode = 500;
|
||||
await context.Response.WriteAsync("downstream path didnt match base path");
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Response.StatusCode = statusCode;
|
||||
await context.Response.WriteAsync(responseBody);
|
||||
}
|
||||
});
|
||||
})
|
||||
.Build();
|
||||
|
||||
_builder.Start();
|
||||
}
|
||||
|
||||
internal void ThenTheDownstreamUrlPathShouldBe(string expectedDownstreamPath)
|
||||
{
|
||||
_downstreamPath.ShouldBe(expectedDownstreamPath);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_builder?.Dispose();
|
||||
_steps.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
@ -237,8 +237,8 @@ namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
s.AddSingleton(_webHostBuilder);
|
||||
s.AddOcelot()
|
||||
.AddSingletonDelegatingHandler<TOne>()
|
||||
.AddSingletonDelegatingHandler<TWo>();
|
||||
.AddDelegatingHandler<TOne>()
|
||||
.AddDelegatingHandler<TWo>();
|
||||
})
|
||||
.Configure(a =>
|
||||
{
|
||||
@ -303,8 +303,39 @@ namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
s.AddSingleton(_webHostBuilder);
|
||||
s.AddOcelot()
|
||||
.AddSingletonDelegatingHandler<TOne>(true)
|
||||
.AddSingletonDelegatingHandler<TWo>(true);
|
||||
.AddDelegatingHandler<TOne>(true)
|
||||
.AddDelegatingHandler<TWo>(true);
|
||||
})
|
||||
.Configure(a =>
|
||||
{
|
||||
a.UseOcelot().Wait();
|
||||
});
|
||||
|
||||
_ocelotServer = new TestServer(_webHostBuilder);
|
||||
|
||||
_ocelotClient = _ocelotServer.CreateClient();
|
||||
}
|
||||
|
||||
public void GivenOcelotIsRunningWithGlobalHandlerRegisteredInDi<TOne>()
|
||||
where TOne : DelegatingHandler
|
||||
{
|
||||
_webHostBuilder = new WebHostBuilder();
|
||||
|
||||
_webHostBuilder
|
||||
.ConfigureAppConfiguration((hostingContext, config) =>
|
||||
{
|
||||
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||
config.AddJsonFile("ocelot.json");
|
||||
config.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(s =>
|
||||
{
|
||||
s.AddSingleton(_webHostBuilder);
|
||||
s.AddOcelot()
|
||||
.AddDelegatingHandler<TOne>(true);
|
||||
})
|
||||
.Configure(a =>
|
||||
{
|
||||
@ -336,7 +367,7 @@ namespace Ocelot.AcceptanceTests
|
||||
s.AddSingleton(_webHostBuilder);
|
||||
s.AddSingleton<FakeDependency>(dependency);
|
||||
s.AddOcelot()
|
||||
.AddSingletonDelegatingHandler<TOne>(true);
|
||||
.AddDelegatingHandler<TOne>(true);
|
||||
})
|
||||
.Configure(a =>
|
||||
{
|
||||
|
@ -2,8 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<VersionPrefix>0.0.0-dev</VersionPrefix>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<RuntimeFrameworkVersion>2.0.0</RuntimeFrameworkVersion>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<AssemblyName>Ocelot.Benchmarks</AssemblyName>
|
||||
<OutputType>Exe</OutputType>
|
||||
<PackageId>Ocelot.Benchmarks</PackageId>
|
||||
|
@ -14,6 +14,7 @@ namespace Ocelot.Benchmarks
|
||||
private RegExUrlMatcher _urlPathMatcher;
|
||||
private string _downstreamUrlPath;
|
||||
private string _downstreamUrlPathTemplate;
|
||||
private string _upstreamQuery;
|
||||
|
||||
public UrlPathToUrlPathTemplateMatcherBenchmarks()
|
||||
{
|
||||
@ -33,7 +34,7 @@ namespace Ocelot.Benchmarks
|
||||
[Benchmark(Baseline = true)]
|
||||
public void Baseline()
|
||||
{
|
||||
_urlPathMatcher.Match(_downstreamUrlPath, _downstreamUrlPathTemplate);
|
||||
_urlPathMatcher.Match(_downstreamUrlPath, _upstreamQuery, _downstreamUrlPathTemplate, false);
|
||||
}
|
||||
|
||||
// * Summary *
|
||||
|
@ -1,8 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<VersionPrefix>0.0.0-dev</VersionPrefix>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<RuntimeFrameworkVersion>2.0.0</RuntimeFrameworkVersion>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<AssemblyName>Ocelot.IntegrationTests</AssemblyName>
|
||||
<OutputType>Exe</OutputType>
|
||||
<PackageId>Ocelot.IntegrationTests</PackageId>
|
||||
@ -25,26 +24,26 @@
|
||||
<ProjectReference Include="..\..\src\Ocelot\Ocelot.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.8" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.2" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.DotNet.InternalAbstractions" Version="1.0.500-preview2-1-003177" />
|
||||
<PackageReference Include="xunit" Version="2.3.1" />
|
||||
<PackageReference Include="IdentityServer4" Version="2.2.0" />
|
||||
<PackageReference Include="Shouldly" Version="3.0.0" />
|
||||
<PackageReference Include="TestStack.BDDfy" Version="4.3.2" />
|
||||
<PackageReference Include="Consul" Version="0.7.2.4" />
|
||||
<PackageReference Include="Consul" Version="0.7.2.5" />
|
||||
<PackageReference Include="Rafty" Version="0.4.4" />
|
||||
<PackageReference Include="Microsoft.Data.SQLite" Version="2.0.1" />
|
||||
<PackageReference Include="Microsoft.Data.SQLite" Version="2.1.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -1,8 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<PropertyGroup>
|
||||
<VersionPrefix>0.0.0-dev</VersionPrefix>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<RuntimeFrameworkVersion>2.0.0</RuntimeFrameworkVersion>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||
<AssemblyName>Ocelot.ManualTest</AssemblyName>
|
||||
<OutputType>Exe</OutputType>
|
||||
@ -15,6 +14,11 @@
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="ocelot.json">
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="ocelot.json;appsettings.json;idsrv3test.pfx">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
@ -24,15 +28,15 @@
|
||||
<ProjectReference Include="..\..\src\Ocelot\Ocelot.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.2" />
|
||||
<PackageReference Include="Consul" Version="0.7.2.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.1.1" />
|
||||
<PackageReference Include="Consul" Version="0.7.2.5" />
|
||||
<PackageReference Include="Polly" Version="6.0.1" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
@ -1,61 +1,78 @@
|
||||
namespace Ocelot.ManualTest
|
||||
{
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Ocelot.DependencyInjection;
|
||||
using Ocelot.Middleware;
|
||||
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
new WebHostBuilder()
|
||||
.UseKestrel()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.ConfigureAppConfiguration((hostingContext, config) =>
|
||||
{
|
||||
config
|
||||
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
|
||||
.AddJsonFile("appsettings.json", true, true)
|
||||
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
||||
.AddJsonFile("ocelot.json")
|
||||
.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(s => {
|
||||
s.AddAuthentication()
|
||||
.AddJwtBearer("TestKey", x =>
|
||||
{
|
||||
x.Authority = "test";
|
||||
x.Audience = "test";
|
||||
});
|
||||
|
||||
s.AddOcelot()
|
||||
.AddCacheManager(x =>
|
||||
{
|
||||
x.WithDictionaryHandle();
|
||||
})
|
||||
/*.AddOpenTracing(option =>
|
||||
{
|
||||
option.CollectorUrl = "http://localhost:9618";
|
||||
option.Service = "Ocelot.ManualTest";
|
||||
})*/
|
||||
.AddAdministration("/administration", "secret");
|
||||
})
|
||||
.ConfigureLogging((hostingContext, logging) =>
|
||||
{
|
||||
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
|
||||
logging.AddConsole();
|
||||
})
|
||||
.UseIISIntegration()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseOcelot().Wait();
|
||||
})
|
||||
.Build()
|
||||
.Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
namespace Ocelot.ManualTest
|
||||
{
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Ocelot.DependencyInjection;
|
||||
using Ocelot.Middleware;
|
||||
using System;
|
||||
using IdentityServer4.AccessTokenValidation;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
new WebHostBuilder()
|
||||
.UseKestrel()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.ConfigureAppConfiguration((hostingContext, config) =>
|
||||
{
|
||||
config
|
||||
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
|
||||
.AddJsonFile("appsettings.json", true, true)
|
||||
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
||||
.AddJsonFile("ocelot.json")
|
||||
.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(s => {
|
||||
s.AddAuthentication()
|
||||
.AddJwtBearer("TestKey", x =>
|
||||
{
|
||||
x.Authority = "test";
|
||||
x.Audience = "test";
|
||||
});
|
||||
|
||||
s.AddOcelot()
|
||||
.AddDelegatingHandler<FakeHandler>(true)
|
||||
// .AddCacheManager(x =>
|
||||
// {
|
||||
// x.WithDictionaryHandle();
|
||||
// })
|
||||
/*.AddOpenTracing(option =>
|
||||
{
|
||||
option.CollectorUrl = "http://localhost:9618";
|
||||
option.Service = "Ocelot.ManualTest";
|
||||
})*/
|
||||
.AddAdministration("/administration", "secret");
|
||||
})
|
||||
.ConfigureLogging((hostingContext, logging) =>
|
||||
{
|
||||
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
|
||||
logging.AddConsole();
|
||||
})
|
||||
.UseIISIntegration()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseOcelot().Wait();
|
||||
})
|
||||
.Build()
|
||||
.Run();
|
||||
}
|
||||
}
|
||||
|
||||
public class FakeHandler : DelegatingHandler
|
||||
{
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
Console.WriteLine(request.RequestUri);
|
||||
|
||||
//do stuff and optionally call the base handler..
|
||||
return await base.SendAsync(request, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,334 +1,345 @@
|
||||
{
|
||||
"ReRoutes": [
|
||||
{
|
||||
"DownstreamPathTemplate": "/profile",
|
||||
"DownstreamScheme": "http",
|
||||
"UpstreamPathTemplate": "/profile",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "localhost",
|
||||
"Port": 3000
|
||||
"ReRoutes": [
|
||||
{
|
||||
"DownstreamPathTemplate": "/profile",
|
||||
"DownstreamScheme": "http",
|
||||
"UpstreamPathTemplate": "/profile",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "localhost",
|
||||
"Port": 3000
|
||||
}
|
||||
],
|
||||
"QoSOptions": {
|
||||
"TimeoutValue": 360000
|
||||
}
|
||||
],
|
||||
"QoSOptions": {
|
||||
"TimeoutValue": 360000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/values",
|
||||
"DownstreamScheme": "http",
|
||||
"UpstreamPathTemplate": "/api/values",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "localhost",
|
||||
"Port": 5007
|
||||
}
|
||||
],
|
||||
"HttpHandlerOptions": {
|
||||
"AllowAutoRedirect": true,
|
||||
"UseCookieContainer": true,
|
||||
"UseTracing": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "localhost",
|
||||
"Port": 52876
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/identityserverexample",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
},
|
||||
"AuthenticationOptions": {
|
||||
"AuthenticationProviderKey": "TestKey",
|
||||
"AllowedScopes": [
|
||||
"openid",
|
||||
"offline_access"
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/v1/todo/",
|
||||
"DownstreamScheme": "http",
|
||||
"UpstreamPathTemplate": "/api/v1/todo/",
|
||||
"UpstreamHttpMethod": [ "Get", "Post" ],
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "lxtodo.azurewebsites.net",
|
||||
"Port": 80
|
||||
}
|
||||
|
||||
],
|
||||
"DownstreamHeaderTransform": {
|
||||
"Location": "{DownstreamBaseUrl}, {BaseUrl}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/values",
|
||||
"DownstreamScheme": "https",
|
||||
"UpstreamPathTemplate": "/api/values",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "testapivalues.azurewebsites.net",
|
||||
"Port": 443
|
||||
}
|
||||
]
|
||||
},
|
||||
"AddHeadersToRequest": {
|
||||
"CustomerId": "Claims[CustomerId] > value",
|
||||
"LocationId": "Claims[LocationId] > value",
|
||||
"UserType": "Claims[sub] > value[0] > |",
|
||||
"UserId": "Claims[sub] > value[1] > |"
|
||||
{
|
||||
"DownstreamPathTemplate": "/",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "localhost",
|
||||
"Port": 52876
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/identityserverexample",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
},
|
||||
"AuthenticationOptions": {
|
||||
"AuthenticationProviderKey": "TestKey",
|
||||
"AllowedScopes": [
|
||||
"openid",
|
||||
"offline_access"
|
||||
]
|
||||
},
|
||||
"AddHeadersToRequest": {
|
||||
"CustomerId": "Claims[CustomerId] > value",
|
||||
"LocationId": "Claims[LocationId] > value",
|
||||
"UserType": "Claims[sub] > value[0] > |",
|
||||
"UserId": "Claims[sub] > value[1] > |"
|
||||
},
|
||||
"AddClaimsToRequest": {
|
||||
"CustomerId": "Claims[CustomerId] > value",
|
||||
"LocationId": "Claims[LocationId] > value",
|
||||
"UserType": "Claims[sub] > value[0] > |",
|
||||
"UserId": "Claims[sub] > value[1] > |"
|
||||
},
|
||||
"AddQueriesToRequest": {
|
||||
"CustomerId": "Claims[CustomerId] > value",
|
||||
"LocationId": "Claims[LocationId] > value",
|
||||
"UserType": "Claims[sub] > value[0] > |",
|
||||
"UserId": "Claims[sub] > value[1] > |"
|
||||
},
|
||||
"RouteClaimsRequirement": {
|
||||
"UserType": "registered"
|
||||
},
|
||||
"RequestIdKey": "OcRequestId"
|
||||
},
|
||||
"AddClaimsToRequest": {
|
||||
"CustomerId": "Claims[CustomerId] > value",
|
||||
"LocationId": "Claims[LocationId] > value",
|
||||
"UserType": "Claims[sub] > value[0] > |",
|
||||
"UserId": "Claims[sub] > value[1] > |"
|
||||
},
|
||||
"AddQueriesToRequest": {
|
||||
"CustomerId": "Claims[CustomerId] > value",
|
||||
"LocationId": "Claims[LocationId] > value",
|
||||
"UserType": "Claims[sub] > value[0] > |",
|
||||
"UserId": "Claims[sub] > value[1] > |"
|
||||
},
|
||||
"RouteClaimsRequirement": {
|
||||
"UserType": "registered"
|
||||
},
|
||||
"RequestIdKey": "OcRequestId"
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts",
|
||||
"DownstreamScheme": "https",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 443
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts",
|
||||
"DownstreamScheme": "https",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 443
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"HttpHandlerOptions": {
|
||||
"AllowAutoRedirect": true,
|
||||
"UseCookieContainer": true
|
||||
},
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"HttpHandlerOptions": {
|
||||
"AllowAutoRedirect": true,
|
||||
"UseCookieContainer": true
|
||||
},
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts/{postId}",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts/{postId}",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"RequestIdKey": "ReRouteRequestId",
|
||||
"HttpHandlerOptions": {
|
||||
"AllowAutoRedirect": true,
|
||||
"UseCookieContainer": true,
|
||||
"UseTracing": true,
|
||||
"UseProxy": true
|
||||
},
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts/{postId}/comments",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts/{postId}/comments",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"HttpHandlerOptions": {
|
||||
"AllowAutoRedirect": true,
|
||||
"UseCookieContainer": true,
|
||||
"UseTracing": false
|
||||
},
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/comments",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/comments",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts",
|
||||
"UpstreamHttpMethod": [ "Post" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts/{postId}",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts/{postId}",
|
||||
"UpstreamHttpMethod": [ "Put" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts/{postId}",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts/{postId}",
|
||||
"UpstreamHttpMethod": [ "Patch" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts/{postId}",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts/{postId}",
|
||||
"UpstreamHttpMethod": [ "Delete" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/products",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/products",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
},
|
||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/products/{productId}",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/products/{productId}",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/products",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/products",
|
||||
"UpstreamHttpMethod": [ "Post" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/products/{productId}",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/products/{productId}",
|
||||
"UpstreamHttpMethod": [ "Put" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
},
|
||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts/",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
},
|
||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "www.bbc.co.uk",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/bbc/",
|
||||
"UpstreamHttpMethod": [ "Get" ]
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts/{postId}",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts/{postId}",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"RequestIdKey": "ReRouteRequestId",
|
||||
"HttpHandlerOptions": {
|
||||
"AllowAutoRedirect": true,
|
||||
"UseCookieContainer": true,
|
||||
"UseTracing": true,
|
||||
"UseProxy": true
|
||||
},
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts/{postId}/comments",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts/{postId}/comments",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"HttpHandlerOptions": {
|
||||
"AllowAutoRedirect": true,
|
||||
"UseCookieContainer": true,
|
||||
"UseTracing": false
|
||||
},
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/comments",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/comments",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts",
|
||||
"UpstreamHttpMethod": [ "Post" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts/{postId}",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts/{postId}",
|
||||
"UpstreamHttpMethod": [ "Put" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts/{postId}",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts/{postId}",
|
||||
"UpstreamHttpMethod": [ "Patch" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts/{postId}",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts/{postId}",
|
||||
"UpstreamHttpMethod": [ "Delete" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/products",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/products",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
},
|
||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/products/{productId}",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/products/{productId}",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/products",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/products",
|
||||
"UpstreamHttpMethod": [ "Post" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/products/{productId}",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/products/{productId}",
|
||||
"UpstreamHttpMethod": [ "Put" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
},
|
||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts/",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
},
|
||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "www.bbc.co.uk",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/bbc/",
|
||||
"UpstreamHttpMethod": [ "Get" ]
|
||||
],
|
||||
|
||||
"GlobalConfiguration": {
|
||||
"RequestIdKey": "ot-traceid"
|
||||
}
|
||||
],
|
||||
|
||||
"GlobalConfiguration": {
|
||||
"RequestIdKey": "ot-traceid"
|
||||
}
|
||||
}
|
||||
|
@ -599,7 +599,7 @@
|
||||
.WithDownstreamPathTemplate("/products/{productId}")
|
||||
.WithUpstreamPathTemplate("/api/products/{productId}")
|
||||
.WithUpstreamHttpMethod(new List<string> {"Get"})
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("(?i)/api/products/.*/$", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("(?i)/api/products/.*/$", 1, false))
|
||||
.WithLoadBalancerKey("/api/products/{productId}|Get|")
|
||||
.Build();
|
||||
|
||||
@ -628,7 +628,7 @@
|
||||
.WithDownstreamReRoute(downstreamReRoute)
|
||||
.WithUpstreamPathTemplate("/api/products/{productId}")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("(?i)/api/products/.*/$", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("(?i)/api/products/.*/$", 1, false))
|
||||
.Build()
|
||||
}))
|
||||
.BDDfy();
|
||||
@ -922,7 +922,7 @@
|
||||
{
|
||||
_upstreamTemplatePatternCreator
|
||||
.Setup(x => x.Create(It.IsAny<FileReRoute>()))
|
||||
.Returns(new UpstreamPathTemplate(pattern, 1));
|
||||
.Returns(new UpstreamPathTemplate(pattern, 1, false));
|
||||
}
|
||||
|
||||
private void ThenTheRequestIdKeyCreatorIsCalledCorrectly()
|
||||
|
@ -10,7 +10,7 @@ namespace Ocelot.UnitTests.Configuration
|
||||
public class UpstreamTemplatePatternCreatorTests
|
||||
{
|
||||
private FileReRoute _fileReRoute;
|
||||
private UpstreamTemplatePatternCreator _creator;
|
||||
private readonly UpstreamTemplatePatternCreator _creator;
|
||||
private UpstreamPathTemplate _result;
|
||||
|
||||
public UpstreamTemplatePatternCreatorTests()
|
||||
@ -191,6 +191,36 @@ namespace Ocelot.UnitTests.Configuration
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_create_template_pattern_that_matches_query_string()
|
||||
{
|
||||
var fileReRoute = new FileReRoute
|
||||
{
|
||||
UpstreamPathTemplate = "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}"
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
|
||||
.When(x => x.WhenICreateTheTemplatePattern())
|
||||
.Then(x => x.ThenTheFollowingIsReturned("^(?i)/api/subscriptions/.+/updates\\?unitId=.+$"))
|
||||
.And(x => ThenThePriorityIs(1))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_create_template_pattern_that_matches_query_string_with_multiple_params()
|
||||
{
|
||||
var fileReRoute = new FileReRoute
|
||||
{
|
||||
UpstreamPathTemplate = "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}&productId={productId}"
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
|
||||
.When(x => x.WhenICreateTheTemplatePattern())
|
||||
.Then(x => x.ThenTheFollowingIsReturned("^(?i)/api/subscriptions/.+/updates\\?unitId=.+&productId=.+$"))
|
||||
.And(x => ThenThePriorityIs(1))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenTheFollowingFileReRoute(FileReRoute fileReRoute)
|
||||
{
|
||||
_fileReRoute = fileReRoute;
|
||||
|
@ -32,14 +32,29 @@
|
||||
[Fact]
|
||||
public void should_merge_files()
|
||||
{
|
||||
this.Given(_ => GivenMultipleConfigurationFiles())
|
||||
this.Given(_ => GivenMultipleConfigurationFiles(""))
|
||||
.When(_ => WhenIAddOcelotConfiguration())
|
||||
.Then(_ => ThenTheConfigsAreMerged())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenMultipleConfigurationFiles()
|
||||
[Fact]
|
||||
public void should_merge_files_in_specific_folder()
|
||||
{
|
||||
string configFolder = "ConfigFiles";
|
||||
this.Given(_ => GivenMultipleConfigurationFiles(configFolder))
|
||||
.When(_ => WhenIAddOcelotConfigurationWithSpecificFolder(configFolder))
|
||||
.Then(_ => ThenTheConfigsAreMerged())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenMultipleConfigurationFiles(string folder)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(folder))
|
||||
{
|
||||
Directory.CreateDirectory(folder);
|
||||
}
|
||||
|
||||
_globalConfig = new FileConfiguration
|
||||
{
|
||||
GlobalConfiguration = new FileGlobalConfiguration
|
||||
@ -159,10 +174,15 @@
|
||||
}
|
||||
};
|
||||
|
||||
File.WriteAllText("ocelot.global.json", JsonConvert.SerializeObject(_globalConfig));
|
||||
File.WriteAllText("ocelot.reRoutesA.json", JsonConvert.SerializeObject(_reRouteA));
|
||||
File.WriteAllText("ocelot.reRoutesB.json", JsonConvert.SerializeObject(_reRouteB));
|
||||
File.WriteAllText("ocelot.aggregates.json", JsonConvert.SerializeObject(_aggregate));
|
||||
string globalFilename = Path.Combine(folder, "ocelot.global.json");
|
||||
string reroutesAFilename = Path.Combine(folder, "ocelot.reRoutesA.json");
|
||||
string reroutesBFilename = Path.Combine(folder, "ocelot.reRoutesB.json");
|
||||
string aggregatesFilename = Path.Combine(folder, "ocelot.aggregates.json");
|
||||
|
||||
File.WriteAllText(globalFilename, JsonConvert.SerializeObject(_globalConfig));
|
||||
File.WriteAllText(reroutesAFilename, JsonConvert.SerializeObject(_reRouteA));
|
||||
File.WriteAllText(reroutesBFilename, JsonConvert.SerializeObject(_reRouteB));
|
||||
File.WriteAllText(aggregatesFilename, JsonConvert.SerializeObject(_aggregate));
|
||||
}
|
||||
|
||||
private void WhenIAddOcelotConfiguration()
|
||||
@ -172,6 +192,13 @@
|
||||
_configRoot = builder.Build();
|
||||
}
|
||||
|
||||
private void WhenIAddOcelotConfigurationWithSpecificFolder(string folder)
|
||||
{
|
||||
IConfigurationBuilder builder = new ConfigurationBuilder();
|
||||
builder.AddOcelot(folder);
|
||||
_configRoot = builder.Build();
|
||||
}
|
||||
|
||||
private void ThenTheConfigsAreMerged()
|
||||
{
|
||||
var fc = (FileConfiguration)_configRoot.Get(typeof(FileConfiguration));
|
||||
|
@ -1,483 +1,452 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using CacheManager.Core;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Hosting.Internal;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Ocelot.Cache;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.Configuration.Setter;
|
||||
using Ocelot.DependencyInjection;
|
||||
using Ocelot.Requester;
|
||||
using Ocelot.UnitTests.Requester;
|
||||
using Shouldly;
|
||||
using IdentityServer4.AccessTokenValidation;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
using static Ocelot.UnitTests.Middleware.UserDefinedResponseAggregatorTests;
|
||||
using Ocelot.Middleware.Multiplexer;
|
||||
|
||||
namespace Ocelot.UnitTests.DependencyInjection
|
||||
{
|
||||
public class OcelotBuilderTests
|
||||
{
|
||||
private readonly IServiceCollection _services;
|
||||
private IServiceProvider _serviceProvider;
|
||||
private readonly IConfiguration _configRoot;
|
||||
private IOcelotBuilder _ocelotBuilder;
|
||||
private readonly int _maxRetries;
|
||||
private Exception _ex;
|
||||
|
||||
public OcelotBuilderTests()
|
||||
{
|
||||
_configRoot = new ConfigurationRoot(new List<IConfigurationProvider>());
|
||||
_services = new ServiceCollection();
|
||||
_services.AddSingleton<IHostingEnvironment, HostingEnvironment>();
|
||||
_services.AddSingleton(_configRoot);
|
||||
_maxRetries = 100;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_add_specific_delegating_handlers_transient()
|
||||
{
|
||||
this.Given(x => WhenISetUpOcelotServices())
|
||||
.When(x => AddSpecificTransientDelegatingHandler<FakeDelegatingHandler>())
|
||||
.And(x => AddSpecificTransientDelegatingHandler<FakeDelegatingHandlerTwo>())
|
||||
.Then(x => ThenTheProviderIsRegisteredAndReturnsSpecificHandlers<FakeDelegatingHandler, FakeDelegatingHandlerTwo>())
|
||||
.And(x => ThenTheSpecificHandlersAreTransient())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_add_specific_delegating_handler_singleton()
|
||||
{
|
||||
this.Given(x => WhenISetUpOcelotServices())
|
||||
.When(x => AddSpecificDelegatingHandler<FakeDelegatingHandler>())
|
||||
.And(x => AddSpecificDelegatingHandler<FakeDelegatingHandlerTwo>())
|
||||
.Then(x => ThenTheProviderIsRegisteredAndReturnsSpecificHandlers<FakeDelegatingHandler, FakeDelegatingHandlerTwo>())
|
||||
.And(x => ThenTheSpecificHandlersAreSingleton())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_add_global_delegating_handlers_transient()
|
||||
{
|
||||
this.Given(x => WhenISetUpOcelotServices())
|
||||
.When(x => AddTransientGlobalDelegatingHandler<FakeDelegatingHandler>())
|
||||
.And(x => AddTransientGlobalDelegatingHandler<FakeDelegatingHandlerTwo>())
|
||||
.Then(x => ThenTheProviderIsRegisteredAndReturnsHandlers<FakeDelegatingHandler, FakeDelegatingHandlerTwo>())
|
||||
.And(x => ThenTheGlobalHandlersAreTransient())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_add_global_delegating_handlers_singleton()
|
||||
{
|
||||
this.Given(x => WhenISetUpOcelotServices())
|
||||
.When(x => AddGlobalDelegatingHandler<FakeDelegatingHandler>())
|
||||
.And(x => AddGlobalDelegatingHandler<FakeDelegatingHandlerTwo>())
|
||||
.Then(x => ThenTheProviderIsRegisteredAndReturnsHandlers<FakeDelegatingHandler, FakeDelegatingHandlerTwo>())
|
||||
.And(x => ThenTheGlobalHandlersAreSingleton())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_set_up_services()
|
||||
{
|
||||
this.When(x => WhenISetUpOcelotServices())
|
||||
.Then(x => ThenAnExceptionIsntThrown())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_ocelot_builder()
|
||||
{
|
||||
this.When(x => WhenISetUpOcelotServices())
|
||||
.Then(x => ThenAnOcelotBuilderIsReturned())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_set_up_cache_manager()
|
||||
{
|
||||
this.Given(x => WhenISetUpOcelotServices())
|
||||
.When(x => WhenISetUpCacheManager())
|
||||
.Then(x => ThenAnExceptionIsntThrown())
|
||||
.And(x => OnlyOneVersionOfEachCacheIsRegistered())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_set_up_consul()
|
||||
{
|
||||
this.Given(x => WhenISetUpOcelotServices())
|
||||
.When(x => WhenISetUpConsul())
|
||||
.Then(x => ThenAnExceptionIsntThrown())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_set_up_rafty()
|
||||
{
|
||||
this.Given(x => WhenISetUpOcelotServices())
|
||||
.When(x => WhenISetUpRafty())
|
||||
.Then(x => ThenAnExceptionIsntThrown())
|
||||
.Then(x => ThenTheCorrectAdminPathIsRegitered())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_set_up_administration_with_identity_server_options()
|
||||
{
|
||||
Action<IdentityServerAuthenticationOptions> options = o => {};
|
||||
|
||||
this.Given(x => WhenISetUpOcelotServices())
|
||||
.When(x => WhenISetUpAdministration(options))
|
||||
.Then(x => ThenAnExceptionIsntThrown())
|
||||
.Then(x => ThenTheCorrectAdminPathIsRegitered())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_set_up_administration()
|
||||
{
|
||||
this.Given(x => WhenISetUpOcelotServices())
|
||||
.When(x => WhenISetUpAdministration())
|
||||
.Then(x => ThenAnExceptionIsntThrown())
|
||||
.Then(x => ThenTheCorrectAdminPathIsRegitered())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_use_logger_factory()
|
||||
{
|
||||
this.Given(x => WhenISetUpOcelotServices())
|
||||
.When(x => WhenIValidateScopes())
|
||||
.When(x => WhenIAccessLoggerFactory())
|
||||
.Then(x => ThenAnExceptionIsntThrown())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_set_up_tracing()
|
||||
{
|
||||
this.Given(x => WhenISetUpOcelotServices())
|
||||
.When(x => WhenISetUpOpentracing())
|
||||
.When(x => WhenIAccessOcelotHttpTracingHandler())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_set_up_without_passing_in_config()
|
||||
{
|
||||
this.When(x => WhenISetUpOcelotServicesWithoutConfig())
|
||||
.Then(x => ThenAnExceptionIsntThrown())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_add_singleton_defined_aggregators()
|
||||
{
|
||||
this.Given(x => WhenISetUpOcelotServices())
|
||||
.When(x => AddSingletonDefinedAggregator<TestDefinedAggregator>())
|
||||
.When(x => AddSingletonDefinedAggregator<TestDefinedAggregator>())
|
||||
.Then(x => ThenTheProviderIsRegisteredAndReturnsSpecificAggregators<TestDefinedAggregator, TestDefinedAggregator>())
|
||||
.And(x => ThenTheAggregatorsAreSingleton<TestDefinedAggregator, TestDefinedAggregator>())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_add_transient_defined_aggregators()
|
||||
{
|
||||
this.Given(x => WhenISetUpOcelotServices())
|
||||
.When(x => AddTransientDefinedAggregator<TestDefinedAggregator>())
|
||||
.When(x => AddTransientDefinedAggregator<TestDefinedAggregator>())
|
||||
.Then(x => ThenTheProviderIsRegisteredAndReturnsSpecificAggregators<TestDefinedAggregator, TestDefinedAggregator>())
|
||||
.And(x => ThenTheAggregatorsAreTransient<TestDefinedAggregator, TestDefinedAggregator>())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void AddSingletonDefinedAggregator<T>()
|
||||
where T : class, IDefinedAggregator
|
||||
{
|
||||
_ocelotBuilder.AddSingletonDefinedAggregator<T>();
|
||||
}
|
||||
|
||||
private void AddTransientDefinedAggregator<T>()
|
||||
where T : class, IDefinedAggregator
|
||||
{
|
||||
_ocelotBuilder.AddTransientDefinedAggregator<T>();
|
||||
}
|
||||
|
||||
private void ThenTheSpecificHandlersAreSingleton()
|
||||
{
|
||||
var handlers = _serviceProvider.GetServices<DelegatingHandler>().ToList();
|
||||
var first = handlers[0];
|
||||
handlers = _serviceProvider.GetServices<DelegatingHandler>().ToList();
|
||||
var second = handlers[0];
|
||||
first.ShouldBe(second);
|
||||
}
|
||||
|
||||
private void ThenTheSpecificHandlersAreTransient()
|
||||
{
|
||||
var handlers = _serviceProvider.GetServices<DelegatingHandler>().ToList();
|
||||
var first = handlers[0];
|
||||
handlers = _serviceProvider.GetServices<DelegatingHandler>().ToList();
|
||||
var second = handlers[0];
|
||||
first.ShouldNotBe(second);
|
||||
}
|
||||
|
||||
private void ThenTheGlobalHandlersAreSingleton()
|
||||
{
|
||||
var handlers = _serviceProvider.GetServices<GlobalDelegatingHandler>().ToList();
|
||||
var first = handlers[0].DelegatingHandler;
|
||||
handlers = _serviceProvider.GetServices<GlobalDelegatingHandler>().ToList();
|
||||
var second = handlers[0].DelegatingHandler;
|
||||
first.ShouldBe(second);
|
||||
}
|
||||
|
||||
private void ThenTheGlobalHandlersAreTransient()
|
||||
{
|
||||
var handlers = _serviceProvider.GetServices<GlobalDelegatingHandler>().ToList();
|
||||
var first = handlers[0].DelegatingHandler;
|
||||
handlers = _serviceProvider.GetServices<GlobalDelegatingHandler>().ToList();
|
||||
var second = handlers[0].DelegatingHandler;
|
||||
first.ShouldNotBe(second);
|
||||
}
|
||||
|
||||
private void WhenISetUpAdministration()
|
||||
{
|
||||
_ocelotBuilder.AddAdministration("/administration", "secret");
|
||||
}
|
||||
|
||||
private void WhenISetUpAdministration(Action<IdentityServerAuthenticationOptions> options)
|
||||
{
|
||||
_ocelotBuilder.AddAdministration("/administration", options);
|
||||
}
|
||||
|
||||
private void AddTransientGlobalDelegatingHandler<T>()
|
||||
where T : DelegatingHandler
|
||||
{
|
||||
_ocelotBuilder.AddTransientDelegatingHandler<T>(true);
|
||||
}
|
||||
|
||||
private void AddSpecificTransientDelegatingHandler<T>()
|
||||
where T : DelegatingHandler
|
||||
{
|
||||
_ocelotBuilder.AddTransientDelegatingHandler<T>();
|
||||
}
|
||||
|
||||
private void ThenTheCorrectAdminPathIsRegitered()
|
||||
{
|
||||
_serviceProvider = _services.BuildServiceProvider();
|
||||
var path = _serviceProvider.GetService<IAdministrationPath>();
|
||||
path.Path.ShouldBe("/administration");
|
||||
}
|
||||
|
||||
private void ThenTheProviderIsRegisteredAndReturnsHandlers<TOne, TWo>()
|
||||
{
|
||||
_serviceProvider = _services.BuildServiceProvider();
|
||||
var handlers = _serviceProvider.GetServices<GlobalDelegatingHandler>().ToList();
|
||||
handlers[0].DelegatingHandler.ShouldBeOfType<TOne>();
|
||||
handlers[1].DelegatingHandler.ShouldBeOfType<TWo>();
|
||||
}
|
||||
|
||||
private void ThenTheProviderIsRegisteredAndReturnsSpecificHandlers<TOne, TWo>()
|
||||
{
|
||||
_serviceProvider = _services.BuildServiceProvider();
|
||||
var handlers = _serviceProvider.GetServices<DelegatingHandler>().ToList();
|
||||
handlers[0].ShouldBeOfType<TOne>();
|
||||
handlers[1].ShouldBeOfType<TWo>();
|
||||
}
|
||||
|
||||
private void ThenTheProviderIsRegisteredAndReturnsSpecificAggregators<TOne, TWo>()
|
||||
{
|
||||
_serviceProvider = _services.BuildServiceProvider();
|
||||
var handlers = _serviceProvider.GetServices<IDefinedAggregator>().ToList();
|
||||
handlers[0].ShouldBeOfType<TOne>();
|
||||
handlers[1].ShouldBeOfType<TWo>();
|
||||
}
|
||||
|
||||
private void ThenTheAggregatorsAreTransient<TOne, TWo>()
|
||||
{
|
||||
var aggregators = _serviceProvider.GetServices<IDefinedAggregator>().ToList();
|
||||
var first = aggregators[0];
|
||||
aggregators = _serviceProvider.GetServices<IDefinedAggregator>().ToList();
|
||||
var second = aggregators[0];
|
||||
first.ShouldNotBe(second);
|
||||
}
|
||||
|
||||
private void ThenTheAggregatorsAreSingleton<TOne, TWo>()
|
||||
{
|
||||
var aggregators = _serviceProvider.GetServices<IDefinedAggregator>().ToList();
|
||||
var first = aggregators[0];
|
||||
aggregators = _serviceProvider.GetServices<IDefinedAggregator>().ToList();
|
||||
var second = aggregators[0];
|
||||
first.ShouldBe(second);
|
||||
}
|
||||
|
||||
private void OnlyOneVersionOfEachCacheIsRegistered()
|
||||
{
|
||||
var outputCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache<CachedResponse>));
|
||||
var outputCacheManager = _services.Single(x => x.ServiceType == typeof(ICacheManager<CachedResponse>));
|
||||
var instance = (ICacheManager<CachedResponse>)outputCacheManager.ImplementationInstance;
|
||||
var ocelotConfigCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache<IInternalConfiguration>));
|
||||
var ocelotConfigCacheManager = _services.Single(x => x.ServiceType == typeof(ICacheManager<IInternalConfiguration>));
|
||||
var fileConfigCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache<FileConfiguration>));
|
||||
var fileConfigCacheManager = _services.Single(x => x.ServiceType == typeof(ICacheManager<FileConfiguration>));
|
||||
|
||||
instance.Configuration.MaxRetries.ShouldBe(_maxRetries);
|
||||
outputCache.ShouldNotBeNull();
|
||||
ocelotConfigCache.ShouldNotBeNull();
|
||||
ocelotConfigCacheManager.ShouldNotBeNull();
|
||||
fileConfigCache.ShouldNotBeNull();
|
||||
fileConfigCacheManager.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
private void WhenISetUpConsul()
|
||||
{
|
||||
try
|
||||
{
|
||||
_ocelotBuilder.AddStoreOcelotConfigurationInConsul();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_ex = e;
|
||||
}
|
||||
}
|
||||
|
||||
private void WhenISetUpRafty()
|
||||
{
|
||||
try
|
||||
{
|
||||
_ocelotBuilder.AddAdministration("/administration", "secret").AddRafty();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_ex = e;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddGlobalDelegatingHandler<T>()
|
||||
where T : DelegatingHandler
|
||||
{
|
||||
_ocelotBuilder.AddSingletonDelegatingHandler<T>(true);
|
||||
}
|
||||
|
||||
private void AddSpecificDelegatingHandler<T>()
|
||||
where T : DelegatingHandler
|
||||
{
|
||||
_ocelotBuilder.AddSingletonDelegatingHandler<T>();
|
||||
}
|
||||
|
||||
private void ThenAnOcelotBuilderIsReturned()
|
||||
{
|
||||
_ocelotBuilder.ShouldBeOfType<OcelotBuilder>();
|
||||
}
|
||||
|
||||
private void WhenISetUpOcelotServices()
|
||||
{
|
||||
try
|
||||
{
|
||||
_ocelotBuilder = _services.AddOcelot(_configRoot);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_ex = e;
|
||||
}
|
||||
}
|
||||
|
||||
private void WhenISetUpOcelotServicesWithoutConfig()
|
||||
{
|
||||
try
|
||||
{
|
||||
_ocelotBuilder = _services.AddOcelot();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_ex = e;
|
||||
}
|
||||
}
|
||||
|
||||
private void WhenISetUpCacheManager()
|
||||
{
|
||||
try
|
||||
{
|
||||
_ocelotBuilder.AddCacheManager(x => {
|
||||
x.WithMaxRetries(_maxRetries);
|
||||
x.WithDictionaryHandle();
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_ex = e;
|
||||
}
|
||||
}
|
||||
|
||||
private void WhenISetUpOpentracing()
|
||||
{
|
||||
try
|
||||
{
|
||||
_ocelotBuilder.AddOpenTracing(
|
||||
option =>
|
||||
{
|
||||
option.CollectorUrl = "http://localhost:9618";
|
||||
option.Service = "Ocelot.ManualTest";
|
||||
}
|
||||
);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_ex = e;
|
||||
}
|
||||
}
|
||||
|
||||
private void WhenIAccessLoggerFactory()
|
||||
{
|
||||
try
|
||||
{
|
||||
_serviceProvider = _services.BuildServiceProvider();
|
||||
var logger = _serviceProvider.GetService<IFileConfigurationSetter>();
|
||||
logger.ShouldNotBeNull();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_ex = e;
|
||||
}
|
||||
}
|
||||
|
||||
private void WhenIAccessOcelotHttpTracingHandler()
|
||||
{
|
||||
try
|
||||
{
|
||||
var tracingHandler = _serviceProvider.GetService<OcelotHttpTracingHandler>();
|
||||
tracingHandler.ShouldNotBeNull();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_ex = e;
|
||||
}
|
||||
}
|
||||
|
||||
private void WhenIValidateScopes()
|
||||
{
|
||||
try
|
||||
{
|
||||
_serviceProvider = _services.BuildServiceProvider(new ServiceProviderOptions { ValidateScopes = true });
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_ex = e;
|
||||
}
|
||||
}
|
||||
|
||||
private void ThenAnExceptionIsntThrown()
|
||||
{
|
||||
_ex.ShouldBeNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using CacheManager.Core;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Hosting.Internal;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Ocelot.Cache;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.Configuration.Setter;
|
||||
using Ocelot.DependencyInjection;
|
||||
using Ocelot.Requester;
|
||||
using Ocelot.UnitTests.Requester;
|
||||
using Shouldly;
|
||||
using IdentityServer4.AccessTokenValidation;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
using static Ocelot.UnitTests.Middleware.UserDefinedResponseAggregatorTests;
|
||||
using Ocelot.Middleware.Multiplexer;
|
||||
|
||||
namespace Ocelot.UnitTests.DependencyInjection
|
||||
{
|
||||
public class OcelotBuilderTests
|
||||
{
|
||||
private readonly IServiceCollection _services;
|
||||
private IServiceProvider _serviceProvider;
|
||||
private readonly IConfiguration _configRoot;
|
||||
private IOcelotBuilder _ocelotBuilder;
|
||||
private readonly int _maxRetries;
|
||||
private Exception _ex;
|
||||
|
||||
public OcelotBuilderTests()
|
||||
{
|
||||
_configRoot = new ConfigurationRoot(new List<IConfigurationProvider>());
|
||||
_services = new ServiceCollection();
|
||||
_services.AddSingleton<IHostingEnvironment, HostingEnvironment>();
|
||||
_services.AddSingleton(_configRoot);
|
||||
_maxRetries = 100;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_add_specific_delegating_handlers_transient()
|
||||
{
|
||||
this.Given(x => WhenISetUpOcelotServices())
|
||||
.When(x => AddSpecificTransientDelegatingHandler<FakeDelegatingHandler>())
|
||||
.And(x => AddSpecificTransientDelegatingHandler<FakeDelegatingHandlerTwo>())
|
||||
.Then(x => ThenTheProviderIsRegisteredAndReturnsSpecificHandlers<FakeDelegatingHandler, FakeDelegatingHandlerTwo>())
|
||||
.And(x => ThenTheSpecificHandlersAreTransient())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_add_global_delegating_handlers_transient()
|
||||
{
|
||||
this.Given(x => WhenISetUpOcelotServices())
|
||||
.When(x => AddTransientGlobalDelegatingHandler<FakeDelegatingHandler>())
|
||||
.And(x => AddTransientGlobalDelegatingHandler<FakeDelegatingHandlerTwo>())
|
||||
.Then(x => ThenTheProviderIsRegisteredAndReturnsHandlers<FakeDelegatingHandler, FakeDelegatingHandlerTwo>())
|
||||
.And(x => ThenTheGlobalHandlersAreTransient())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_set_up_services()
|
||||
{
|
||||
this.When(x => WhenISetUpOcelotServices())
|
||||
.Then(x => ThenAnExceptionIsntThrown())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_ocelot_builder()
|
||||
{
|
||||
this.When(x => WhenISetUpOcelotServices())
|
||||
.Then(x => ThenAnOcelotBuilderIsReturned())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_set_up_cache_manager()
|
||||
{
|
||||
this.Given(x => WhenISetUpOcelotServices())
|
||||
.When(x => WhenISetUpCacheManager())
|
||||
.Then(x => ThenAnExceptionIsntThrown())
|
||||
.And(x => OnlyOneVersionOfEachCacheIsRegistered())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_set_up_consul()
|
||||
{
|
||||
this.Given(x => WhenISetUpOcelotServices())
|
||||
.When(x => WhenISetUpConsul())
|
||||
.Then(x => ThenAnExceptionIsntThrown())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_set_up_rafty()
|
||||
{
|
||||
this.Given(x => WhenISetUpOcelotServices())
|
||||
.When(x => WhenISetUpRafty())
|
||||
.Then(x => ThenAnExceptionIsntThrown())
|
||||
.Then(x => ThenTheCorrectAdminPathIsRegitered())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_set_up_administration_with_identity_server_options()
|
||||
{
|
||||
Action<IdentityServerAuthenticationOptions> options = o => {};
|
||||
|
||||
this.Given(x => WhenISetUpOcelotServices())
|
||||
.When(x => WhenISetUpAdministration(options))
|
||||
.Then(x => ThenAnExceptionIsntThrown())
|
||||
.Then(x => ThenTheCorrectAdminPathIsRegitered())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_set_up_administration()
|
||||
{
|
||||
this.Given(x => WhenISetUpOcelotServices())
|
||||
.When(x => WhenISetUpAdministration())
|
||||
.Then(x => ThenAnExceptionIsntThrown())
|
||||
.Then(x => ThenTheCorrectAdminPathIsRegitered())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_use_logger_factory()
|
||||
{
|
||||
this.Given(x => WhenISetUpOcelotServices())
|
||||
.When(x => WhenIValidateScopes())
|
||||
.When(x => WhenIAccessLoggerFactory())
|
||||
.Then(x => ThenAnExceptionIsntThrown())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_set_up_tracing()
|
||||
{
|
||||
this.Given(x => WhenISetUpOcelotServices())
|
||||
.When(x => WhenISetUpOpentracing())
|
||||
.When(x => WhenIAccessOcelotHttpTracingHandler())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_set_up_without_passing_in_config()
|
||||
{
|
||||
this.When(x => WhenISetUpOcelotServicesWithoutConfig())
|
||||
.Then(x => ThenAnExceptionIsntThrown())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_add_singleton_defined_aggregators()
|
||||
{
|
||||
this.Given(x => WhenISetUpOcelotServices())
|
||||
.When(x => AddSingletonDefinedAggregator<TestDefinedAggregator>())
|
||||
.When(x => AddSingletonDefinedAggregator<TestDefinedAggregator>())
|
||||
.Then(x => ThenTheProviderIsRegisteredAndReturnsSpecificAggregators<TestDefinedAggregator, TestDefinedAggregator>())
|
||||
.And(x => ThenTheAggregatorsAreSingleton<TestDefinedAggregator, TestDefinedAggregator>())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_add_transient_defined_aggregators()
|
||||
{
|
||||
this.Given(x => WhenISetUpOcelotServices())
|
||||
.When(x => AddTransientDefinedAggregator<TestDefinedAggregator>())
|
||||
.When(x => AddTransientDefinedAggregator<TestDefinedAggregator>())
|
||||
.Then(x => ThenTheProviderIsRegisteredAndReturnsSpecificAggregators<TestDefinedAggregator, TestDefinedAggregator>())
|
||||
.And(x => ThenTheAggregatorsAreTransient<TestDefinedAggregator, TestDefinedAggregator>())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void AddSingletonDefinedAggregator<T>()
|
||||
where T : class, IDefinedAggregator
|
||||
{
|
||||
_ocelotBuilder.AddSingletonDefinedAggregator<T>();
|
||||
}
|
||||
|
||||
private void AddTransientDefinedAggregator<T>()
|
||||
where T : class, IDefinedAggregator
|
||||
{
|
||||
_ocelotBuilder.AddTransientDefinedAggregator<T>();
|
||||
}
|
||||
|
||||
private void ThenTheSpecificHandlersAreSingleton()
|
||||
{
|
||||
var handlers = _serviceProvider.GetServices<DelegatingHandler>().ToList();
|
||||
var first = handlers[0];
|
||||
handlers = _serviceProvider.GetServices<DelegatingHandler>().ToList();
|
||||
var second = handlers[0];
|
||||
first.ShouldBe(second);
|
||||
}
|
||||
|
||||
private void ThenTheSpecificHandlersAreTransient()
|
||||
{
|
||||
var handlers = _serviceProvider.GetServices<DelegatingHandler>().ToList();
|
||||
var first = handlers[0];
|
||||
handlers = _serviceProvider.GetServices<DelegatingHandler>().ToList();
|
||||
var second = handlers[0];
|
||||
first.ShouldNotBe(second);
|
||||
}
|
||||
|
||||
private void ThenTheGlobalHandlersAreTransient()
|
||||
{
|
||||
var handlers = _serviceProvider.GetServices<GlobalDelegatingHandler>().ToList();
|
||||
var first = handlers[0].DelegatingHandler;
|
||||
handlers = _serviceProvider.GetServices<GlobalDelegatingHandler>().ToList();
|
||||
var second = handlers[0].DelegatingHandler;
|
||||
first.ShouldNotBe(second);
|
||||
}
|
||||
|
||||
private void WhenISetUpAdministration()
|
||||
{
|
||||
_ocelotBuilder.AddAdministration("/administration", "secret");
|
||||
}
|
||||
|
||||
private void WhenISetUpAdministration(Action<IdentityServerAuthenticationOptions> options)
|
||||
{
|
||||
_ocelotBuilder.AddAdministration("/administration", options);
|
||||
}
|
||||
|
||||
private void AddTransientGlobalDelegatingHandler<T>()
|
||||
where T : DelegatingHandler
|
||||
{
|
||||
_ocelotBuilder.AddDelegatingHandler<T>(true);
|
||||
}
|
||||
|
||||
private void AddSpecificTransientDelegatingHandler<T>()
|
||||
where T : DelegatingHandler
|
||||
{
|
||||
_ocelotBuilder.AddDelegatingHandler<T>();
|
||||
}
|
||||
|
||||
private void ThenTheCorrectAdminPathIsRegitered()
|
||||
{
|
||||
_serviceProvider = _services.BuildServiceProvider();
|
||||
var path = _serviceProvider.GetService<IAdministrationPath>();
|
||||
path.Path.ShouldBe("/administration");
|
||||
}
|
||||
|
||||
private void ThenTheProviderIsRegisteredAndReturnsHandlers<TOne, TWo>()
|
||||
{
|
||||
_serviceProvider = _services.BuildServiceProvider();
|
||||
var handlers = _serviceProvider.GetServices<GlobalDelegatingHandler>().ToList();
|
||||
handlers[0].DelegatingHandler.ShouldBeOfType<TOne>();
|
||||
handlers[1].DelegatingHandler.ShouldBeOfType<TWo>();
|
||||
}
|
||||
|
||||
private void ThenTheProviderIsRegisteredAndReturnsSpecificHandlers<TOne, TWo>()
|
||||
{
|
||||
_serviceProvider = _services.BuildServiceProvider();
|
||||
var handlers = _serviceProvider.GetServices<DelegatingHandler>().ToList();
|
||||
handlers[0].ShouldBeOfType<TOne>();
|
||||
handlers[1].ShouldBeOfType<TWo>();
|
||||
}
|
||||
|
||||
private void ThenTheProviderIsRegisteredAndReturnsSpecificAggregators<TOne, TWo>()
|
||||
{
|
||||
_serviceProvider = _services.BuildServiceProvider();
|
||||
var handlers = _serviceProvider.GetServices<IDefinedAggregator>().ToList();
|
||||
handlers[0].ShouldBeOfType<TOne>();
|
||||
handlers[1].ShouldBeOfType<TWo>();
|
||||
}
|
||||
|
||||
private void ThenTheAggregatorsAreTransient<TOne, TWo>()
|
||||
{
|
||||
var aggregators = _serviceProvider.GetServices<IDefinedAggregator>().ToList();
|
||||
var first = aggregators[0];
|
||||
aggregators = _serviceProvider.GetServices<IDefinedAggregator>().ToList();
|
||||
var second = aggregators[0];
|
||||
first.ShouldNotBe(second);
|
||||
}
|
||||
|
||||
private void ThenTheAggregatorsAreSingleton<TOne, TWo>()
|
||||
{
|
||||
var aggregators = _serviceProvider.GetServices<IDefinedAggregator>().ToList();
|
||||
var first = aggregators[0];
|
||||
aggregators = _serviceProvider.GetServices<IDefinedAggregator>().ToList();
|
||||
var second = aggregators[0];
|
||||
first.ShouldBe(second);
|
||||
}
|
||||
|
||||
private void OnlyOneVersionOfEachCacheIsRegistered()
|
||||
{
|
||||
var outputCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache<CachedResponse>));
|
||||
var outputCacheManager = _services.Single(x => x.ServiceType == typeof(ICacheManager<CachedResponse>));
|
||||
var instance = (ICacheManager<CachedResponse>)outputCacheManager.ImplementationInstance;
|
||||
var ocelotConfigCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache<IInternalConfiguration>));
|
||||
var ocelotConfigCacheManager = _services.Single(x => x.ServiceType == typeof(ICacheManager<IInternalConfiguration>));
|
||||
var fileConfigCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache<FileConfiguration>));
|
||||
var fileConfigCacheManager = _services.Single(x => x.ServiceType == typeof(ICacheManager<FileConfiguration>));
|
||||
|
||||
instance.Configuration.MaxRetries.ShouldBe(_maxRetries);
|
||||
outputCache.ShouldNotBeNull();
|
||||
ocelotConfigCache.ShouldNotBeNull();
|
||||
ocelotConfigCacheManager.ShouldNotBeNull();
|
||||
fileConfigCache.ShouldNotBeNull();
|
||||
fileConfigCacheManager.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
private void WhenISetUpConsul()
|
||||
{
|
||||
try
|
||||
{
|
||||
_ocelotBuilder.AddStoreOcelotConfigurationInConsul();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_ex = e;
|
||||
}
|
||||
}
|
||||
|
||||
private void WhenISetUpRafty()
|
||||
{
|
||||
try
|
||||
{
|
||||
_ocelotBuilder.AddAdministration("/administration", "secret").AddRafty();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_ex = e;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddGlobalDelegatingHandler<T>()
|
||||
where T : DelegatingHandler
|
||||
{
|
||||
_ocelotBuilder.AddDelegatingHandler<T>(true);
|
||||
}
|
||||
|
||||
private void AddSpecificDelegatingHandler<T>()
|
||||
where T : DelegatingHandler
|
||||
{
|
||||
_ocelotBuilder.AddDelegatingHandler<T>();
|
||||
}
|
||||
|
||||
private void ThenAnOcelotBuilderIsReturned()
|
||||
{
|
||||
_ocelotBuilder.ShouldBeOfType<OcelotBuilder>();
|
||||
}
|
||||
|
||||
private void WhenISetUpOcelotServices()
|
||||
{
|
||||
try
|
||||
{
|
||||
_ocelotBuilder = _services.AddOcelot(_configRoot);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_ex = e;
|
||||
}
|
||||
}
|
||||
|
||||
private void WhenISetUpOcelotServicesWithoutConfig()
|
||||
{
|
||||
try
|
||||
{
|
||||
_ocelotBuilder = _services.AddOcelot();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_ex = e;
|
||||
}
|
||||
}
|
||||
|
||||
private void WhenISetUpCacheManager()
|
||||
{
|
||||
try
|
||||
{
|
||||
_ocelotBuilder.AddCacheManager(x => {
|
||||
x.WithMaxRetries(_maxRetries);
|
||||
x.WithDictionaryHandle();
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_ex = e;
|
||||
}
|
||||
}
|
||||
|
||||
private void WhenISetUpOpentracing()
|
||||
{
|
||||
try
|
||||
{
|
||||
_ocelotBuilder.AddOpenTracing(
|
||||
option =>
|
||||
{
|
||||
option.CollectorUrl = "http://localhost:9618";
|
||||
option.Service = "Ocelot.ManualTest";
|
||||
}
|
||||
);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_ex = e;
|
||||
}
|
||||
}
|
||||
|
||||
private void WhenIAccessLoggerFactory()
|
||||
{
|
||||
try
|
||||
{
|
||||
_serviceProvider = _services.BuildServiceProvider();
|
||||
var logger = _serviceProvider.GetService<IFileConfigurationSetter>();
|
||||
logger.ShouldNotBeNull();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_ex = e;
|
||||
}
|
||||
}
|
||||
|
||||
private void WhenIAccessOcelotHttpTracingHandler()
|
||||
{
|
||||
try
|
||||
{
|
||||
var tracingHandler = _serviceProvider.GetService<OcelotHttpTracingHandler>();
|
||||
tracingHandler.ShouldNotBeNull();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_ex = e;
|
||||
}
|
||||
}
|
||||
|
||||
private void WhenIValidateScopes()
|
||||
{
|
||||
try
|
||||
{
|
||||
_serviceProvider = _services.BuildServiceProvider(new ServiceProviderOptions { ValidateScopes = true });
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_ex = e;
|
||||
}
|
||||
}
|
||||
|
||||
private void ThenAnExceptionIsntThrown()
|
||||
{
|
||||
_ex.ShouldBeNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
private IInternalConfiguration _configuration;
|
||||
private Mock<IQoSOptionsCreator> _qosOptionsCreator;
|
||||
private Response<DownstreamRoute> _resultTwo;
|
||||
private string _upstreamQuery;
|
||||
|
||||
public DownstreamRouteCreatorTests()
|
||||
{
|
||||
@ -247,12 +248,12 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
|
||||
private void WhenICreate()
|
||||
{
|
||||
_result = _creator.Get(_upstreamUrlPath, _upstreamHttpMethod, _configuration, _upstreamHost);
|
||||
_result = _creator.Get(_upstreamUrlPath, _upstreamQuery, _upstreamHttpMethod, _configuration, _upstreamHost);
|
||||
}
|
||||
|
||||
private void WhenICreateAgain()
|
||||
{
|
||||
_resultTwo = _creator.Get(_upstreamUrlPath, _upstreamHttpMethod, _configuration, _upstreamHost);
|
||||
_resultTwo = _creator.Get(_upstreamUrlPath, _upstreamQuery, _upstreamHttpMethod, _configuration, _upstreamHost);
|
||||
}
|
||||
|
||||
private void ThenTheDownstreamRoutesAreTheSameReference()
|
||||
|
@ -86,7 +86,7 @@
|
||||
{
|
||||
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
|
||||
_finder
|
||||
.Setup(x => x.Get(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IInternalConfiguration>(), It.IsAny<string>()))
|
||||
.Setup(x => x.Get(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IInternalConfiguration>(), It.IsAny<string>()))
|
||||
.Returns(_downstreamRoute);
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
private Response<UrlMatch> _match;
|
||||
private string _upstreamHttpMethod;
|
||||
private string _upstreamHost;
|
||||
private string _upstreamQuery;
|
||||
|
||||
public DownstreamRouteFinderTests()
|
||||
{
|
||||
@ -48,22 +49,22 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Post" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1, false))
|
||||
.Build())
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Post" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1, false))
|
||||
.Build(),
|
||||
new ReRouteBuilder()
|
||||
.WithDownstreamReRoute(new DownstreamReRouteBuilder()
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Post" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 0))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 0, false))
|
||||
.Build())
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Post" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 0))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 0, false))
|
||||
.Build()
|
||||
}, string.Empty, serviceProviderConfig))
|
||||
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
|
||||
@ -73,13 +74,13 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
new ReRouteBuilder()
|
||||
.WithDownstreamReRoute(new DownstreamReRouteBuilder()
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1, false))
|
||||
.WithUpstreamHttpMethod(new List<string> { "Post" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1, false))
|
||||
.Build())
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1, false))
|
||||
.WithUpstreamHttpMethod(new List<string> { "Post" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1, false))
|
||||
.Build()
|
||||
)))
|
||||
.BDDfy();
|
||||
@ -100,22 +101,22 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Post" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 0))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 0, false))
|
||||
.Build())
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Post" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 0))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 0, false))
|
||||
.Build(),
|
||||
new ReRouteBuilder()
|
||||
.WithDownstreamReRoute(new DownstreamReRouteBuilder()
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Post" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1, false))
|
||||
.Build())
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Post" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1, false))
|
||||
.Build()
|
||||
}, string.Empty, serviceProviderConfig))
|
||||
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
|
||||
@ -125,10 +126,10 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
new ReRouteBuilder()
|
||||
.WithDownstreamReRoute(new DownstreamReRouteBuilder()
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1, false))
|
||||
.WithUpstreamHttpMethod(new List<string> { "Post" })
|
||||
.Build())
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1, false))
|
||||
.WithUpstreamHttpMethod(new List<string> { "Post" })
|
||||
.Build()
|
||||
)))
|
||||
@ -151,11 +152,11 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.Build())
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.Build()
|
||||
}, string.Empty, serviceProviderConfig
|
||||
))
|
||||
@ -169,10 +170,10 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamReRoute(new DownstreamReRouteBuilder()
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.Build())
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.Build()
|
||||
)))
|
||||
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly())
|
||||
@ -195,11 +196,11 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.Build())
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.Build()
|
||||
}, string.Empty, serviceProviderConfig
|
||||
))
|
||||
@ -213,10 +214,10 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamReRoute(new DownstreamReRouteBuilder()
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.Build())
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.Build()
|
||||
)))
|
||||
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly("matchInUrlMatcher"))
|
||||
@ -240,11 +241,11 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.Build())
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.Build()
|
||||
}, string.Empty, serviceProviderConfig
|
||||
))
|
||||
@ -257,10 +258,10 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamReRoute(new DownstreamReRouteBuilder()
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.Build())
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.Build()
|
||||
)))
|
||||
.BDDfy();
|
||||
@ -283,22 +284,22 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1, false))
|
||||
.Build())
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1, false))
|
||||
.Build(),
|
||||
new ReRouteBuilder()
|
||||
.WithDownstreamReRoute(new DownstreamReRouteBuilder()
|
||||
.WithDownstreamPathTemplate("someDownstreamPathForAPost")
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Post" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1, false))
|
||||
.Build())
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Post" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1, false))
|
||||
.Build()
|
||||
}, string.Empty, serviceProviderConfig
|
||||
))
|
||||
@ -311,10 +312,10 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamReRoute(new DownstreamReRouteBuilder()
|
||||
.WithDownstreamPathTemplate("someDownstreamPathForAPost")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Post" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1, false))
|
||||
.Build())
|
||||
.WithUpstreamHttpMethod(new List<string> { "Post" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1, false))
|
||||
.Build()
|
||||
)))
|
||||
.BDDfy();
|
||||
@ -333,11 +334,11 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamPathTemplate("somPath")
|
||||
.WithUpstreamPathTemplate("somePath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("somePath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("somePath", 1, false))
|
||||
.Build())
|
||||
.WithUpstreamPathTemplate("somePath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("somePath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("somePath", 1, false))
|
||||
.Build(),
|
||||
}, string.Empty, serviceProviderConfig
|
||||
))
|
||||
@ -367,11 +368,11 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get", "Post" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1, false))
|
||||
.Build())
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get", "Post" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1, false))
|
||||
.Build()
|
||||
}, string.Empty, serviceProviderConfig
|
||||
))
|
||||
@ -384,10 +385,10 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamReRoute(new DownstreamReRouteBuilder()
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Post" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1, false))
|
||||
.Build())
|
||||
.WithUpstreamHttpMethod(new List<string> { "Post" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1, false))
|
||||
.Build()
|
||||
)))
|
||||
.BDDfy();
|
||||
@ -410,11 +411,11 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string>())
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1, false))
|
||||
.Build())
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string>())
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1, false))
|
||||
.Build()
|
||||
}, string.Empty, serviceProviderConfig
|
||||
))
|
||||
@ -427,10 +428,10 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamReRoute(new DownstreamReRouteBuilder()
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Post" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1, false))
|
||||
.Build())
|
||||
.WithUpstreamHttpMethod(new List<string> { "Post" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1, false))
|
||||
.Build()
|
||||
)))
|
||||
.BDDfy();
|
||||
@ -453,11 +454,11 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get", "Patch", "Delete" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1, false))
|
||||
.Build())
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get", "Patch", "Delete" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1, false))
|
||||
.Build()
|
||||
}, string.Empty, serviceProviderConfig
|
||||
))
|
||||
@ -486,12 +487,12 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.WithUpstreamHost("MATCH")
|
||||
.Build())
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.WithUpstreamHost("MATCH")
|
||||
.Build()
|
||||
}, string.Empty, serviceProviderConfig
|
||||
@ -506,10 +507,10 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamReRoute(new DownstreamReRouteBuilder()
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.Build())
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.Build()
|
||||
)))
|
||||
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly())
|
||||
@ -533,11 +534,11 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.Build())
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.Build()
|
||||
}, string.Empty, serviceProviderConfig
|
||||
))
|
||||
@ -551,10 +552,10 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamReRoute(new DownstreamReRouteBuilder()
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.Build())
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.Build()
|
||||
)))
|
||||
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly())
|
||||
@ -576,12 +577,12 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.WithUpstreamHost("MATCH")
|
||||
.Build())
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.WithUpstreamHost("MATCH")
|
||||
.Build(),
|
||||
new ReRouteBuilder()
|
||||
@ -589,12 +590,12 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { }) // empty list of methods
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.WithUpstreamHost("MATCH")
|
||||
.Build())
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { }) // empty list of methods
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.WithUpstreamHost("MATCH")
|
||||
.Build()
|
||||
}, string.Empty, serviceProviderConfig
|
||||
@ -622,12 +623,12 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string>())
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.WithUpstreamHost("MATCH")
|
||||
.Build())
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string>())
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.WithUpstreamHost("MATCH")
|
||||
.Build()
|
||||
}, string.Empty, serviceProviderConfig
|
||||
@ -655,12 +656,12 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string>())
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.WithUpstreamHost("MATCH")
|
||||
.Build())
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string>())
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.WithUpstreamHost("MATCH")
|
||||
.Build()
|
||||
}, string.Empty, serviceProviderConfig
|
||||
@ -689,23 +690,23 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamPathTemplate("THENULLPATH")
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.Build())
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.Build(),
|
||||
new ReRouteBuilder()
|
||||
.WithDownstreamReRoute(new DownstreamReRouteBuilder()
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.WithUpstreamHost("MATCH")
|
||||
.Build())
|
||||
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.WithUpstreamHost("MATCH")
|
||||
.Build()
|
||||
}, string.Empty, serviceProviderConfig
|
||||
@ -720,10 +721,10 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamReRoute(new DownstreamReRouteBuilder()
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.Build())
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1, false))
|
||||
.Build()
|
||||
)))
|
||||
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly(2))
|
||||
@ -738,7 +739,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
private void GivenTheTemplateVariableAndNameFinderReturns(Response<List<PlaceholderNameAndValue>> response)
|
||||
{
|
||||
_finder
|
||||
.Setup(x => x.Find(It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Setup(x => x.Find(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Returns(response);
|
||||
}
|
||||
|
||||
@ -755,32 +756,32 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
private void ThenTheUrlMatcherIsCalledCorrectly()
|
||||
{
|
||||
_mockMatcher
|
||||
.Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Once);
|
||||
.Verify(x => x.Match(_upstreamUrlPath, _upstreamQuery, _reRoutesConfig[0].UpstreamPathTemplate.Value, _reRoutesConfig[0].UpstreamTemplatePattern.ContainsQueryString), Times.Once);
|
||||
}
|
||||
|
||||
private void ThenTheUrlMatcherIsCalledCorrectly(int times)
|
||||
{
|
||||
_mockMatcher
|
||||
.Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Exactly(times));
|
||||
.Verify(x => x.Match(_upstreamUrlPath, _upstreamQuery, _reRoutesConfig[0].UpstreamPathTemplate.Value, _reRoutesConfig[0].UpstreamTemplatePattern.ContainsQueryString), Times.Exactly(times));
|
||||
}
|
||||
|
||||
private void ThenTheUrlMatcherIsCalledCorrectly(string expectedUpstreamUrlPath)
|
||||
{
|
||||
_mockMatcher
|
||||
.Verify(x => x.Match(expectedUpstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Once);
|
||||
.Verify(x => x.Match(expectedUpstreamUrlPath, _upstreamQuery, _reRoutesConfig[0].UpstreamPathTemplate.Value, _reRoutesConfig[0].UpstreamTemplatePattern.ContainsQueryString), Times.Once);
|
||||
}
|
||||
|
||||
private void ThenTheUrlMatcherIsNotCalled()
|
||||
{
|
||||
_mockMatcher
|
||||
.Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Never);
|
||||
.Verify(x => x.Match(_upstreamUrlPath, _upstreamQuery, _reRoutesConfig[0].UpstreamPathTemplate.Value, _reRoutesConfig[0].UpstreamTemplatePattern.ContainsQueryString), Times.Never);
|
||||
}
|
||||
|
||||
private void GivenTheUrlMatcherReturns(Response<UrlMatch> match)
|
||||
{
|
||||
_match = match;
|
||||
_mockMatcher
|
||||
.Setup(x => x.Match(It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Setup(x => x.Match(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>()))
|
||||
.Returns(_match);
|
||||
}
|
||||
|
||||
@ -797,7 +798,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
|
||||
private void WhenICallTheFinder()
|
||||
{
|
||||
_result = _downstreamRouteFinder.Get(_upstreamUrlPath, _upstreamHttpMethod, _config, _upstreamHost);
|
||||
_result = _downstreamRouteFinder.Get(_upstreamUrlPath, _upstreamQuery, _upstreamHttpMethod, _config, _upstreamHost);
|
||||
}
|
||||
|
||||
private void ThenTheFollowingIsReturned(DownstreamRoute expected)
|
||||
|
@ -1,26 +1,28 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Moq;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.DownstreamRouteFinder;
|
||||
using Ocelot.DownstreamRouteFinder.Finder;
|
||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Moq;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.DownstreamRouteFinder;
|
||||
using Ocelot.DownstreamRouteFinder.Finder;
|
||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
using Ocelot.Configuration.Creator;
|
||||
using Ocelot.Logging;
|
||||
|
||||
public class DownstreamRouteProviderFactoryTests
|
||||
{
|
||||
private readonly DownstreamRouteProviderFactory _factory;
|
||||
private IInternalConfiguration _config;
|
||||
private IDownstreamRouteProvider _result;
|
||||
private Mock<IOcelotLogger> _logger;
|
||||
private Mock<IOcelotLoggerFactory> _loggerFactory;
|
||||
|
||||
public DownstreamRouteProviderFactoryTests()
|
||||
{
|
||||
@ -31,7 +33,10 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
services.AddSingleton<IDownstreamRouteProvider, Ocelot.DownstreamRouteFinder.Finder.DownstreamRouteFinder>();
|
||||
services.AddSingleton<IDownstreamRouteProvider, Ocelot.DownstreamRouteFinder.Finder.DownstreamRouteCreator>();
|
||||
var provider = services.BuildServiceProvider();
|
||||
_factory = new DownstreamRouteProviderFactory(provider);
|
||||
_logger = new Mock<IOcelotLogger>();
|
||||
_loggerFactory = new Mock<IOcelotLoggerFactory>();
|
||||
_loggerFactory.Setup(x => x.CreateLogger<DownstreamRouteProviderFactory>()).Returns(_logger.Object);
|
||||
_factory = new DownstreamRouteProviderFactory(provider, _loggerFactory.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -49,12 +54,34 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_downstream_route_finder_as_no_service_discovery()
|
||||
public void should_return_downstream_route_finder_as_no_service_discovery_given_no_host()
|
||||
{
|
||||
var spConfig = new ServiceProviderConfigurationBuilder().Build();
|
||||
var reRoutes = new List<ReRoute>
|
||||
{
|
||||
};
|
||||
var spConfig = new ServiceProviderConfigurationBuilder().WithHost("").WithPort(50).Build();
|
||||
var reRoutes = new List<ReRoute>();
|
||||
|
||||
this.Given(_ => GivenTheReRoutes(reRoutes, spConfig))
|
||||
.When(_ => WhenIGet())
|
||||
.Then(_ => ThenTheResultShouldBe<Ocelot.DownstreamRouteFinder.Finder.DownstreamRouteFinder>())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_downstream_route_finder_given_no_service_discovery_port()
|
||||
{
|
||||
var spConfig = new ServiceProviderConfigurationBuilder().WithHost("localhost").WithPort(0).Build();
|
||||
var reRoutes = new List<ReRoute>();
|
||||
|
||||
this.Given(_ => GivenTheReRoutes(reRoutes, spConfig))
|
||||
.When(_ => WhenIGet())
|
||||
.Then(_ => ThenTheResultShouldBe<Ocelot.DownstreamRouteFinder.Finder.DownstreamRouteFinder>())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_downstream_route_finder_given_no_service_discovery_type()
|
||||
{
|
||||
var spConfig = new ServiceProviderConfigurationBuilder().WithHost("localhost").WithPort(50).WithType("").Build();
|
||||
var reRoutes = new List<ReRoute>();
|
||||
|
||||
this.Given(_ => GivenTheReRoutes(reRoutes, spConfig))
|
||||
.When(_ => WhenIGet())
|
||||
@ -65,10 +92,9 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
[Fact]
|
||||
public void should_return_downstream_route_creator()
|
||||
{
|
||||
var spConfig = new ServiceProviderConfigurationBuilder().WithHost("test").WithPort(50).Build();
|
||||
var reRoutes = new List<ReRoute>
|
||||
{
|
||||
};
|
||||
var spConfig = new ServiceProviderConfigurationBuilder().WithHost("test").WithPort(50).WithType("test").Build();
|
||||
var reRoutes = new List<ReRoute>();
|
||||
|
||||
this.Given(_ => GivenTheReRoutes(reRoutes, spConfig))
|
||||
.When(_ => WhenIGet())
|
||||
.Then(_ => ThenTheResultShouldBe<Ocelot.DownstreamRouteFinder.Finder.DownstreamRouteCreator>())
|
||||
|
@ -9,22 +9,65 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
|
||||
public class RegExUrlMatcherTests
|
||||
{
|
||||
private readonly IUrlPathToUrlTemplateMatcher _urlMatcher;
|
||||
private string _downstreamUrlPath;
|
||||
private string _path;
|
||||
private string _downstreamPathTemplate;
|
||||
private Response<UrlMatch> _result;
|
||||
private string _queryString;
|
||||
private bool _containsQueryString;
|
||||
|
||||
public RegExUrlMatcherTests()
|
||||
{
|
||||
_urlMatcher = new RegExUrlMatcher();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_match_path_with_no_query_string()
|
||||
{
|
||||
const string regExForwardSlashAndOnePlaceHolder = "^(?i)/newThing$";
|
||||
|
||||
this.Given(x => x.GivenIHaveAUpstreamPath("/newThing"))
|
||||
.And(_ => GivenIHaveAQueryString("?DeviceType=IphoneApp&Browser=moonpigIphone&BrowserString=-&CountryCode=123&DeviceName=iPhone 5 (GSM+CDMA)&OperatingSystem=iPhone OS 7.1.2&BrowserVersion=3708AdHoc&ipAddress=-"))
|
||||
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern(regExForwardSlashAndOnePlaceHolder))
|
||||
.When(x => x.WhenIMatchThePaths())
|
||||
.And(x => x.ThenTheResultIsTrue())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_match_query_string()
|
||||
{
|
||||
const string regExForwardSlashAndOnePlaceHolder = "^(?i)/api/subscriptions/.+/updates\\?unitId=.+$";
|
||||
|
||||
this.Given(x => x.GivenIHaveAUpstreamPath("/api/subscriptions/1/updates"))
|
||||
.And(_ => GivenIHaveAQueryString("?unitId=2"))
|
||||
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern(regExForwardSlashAndOnePlaceHolder))
|
||||
.And(_ => GivenThereIsAQueryInTemplate())
|
||||
.When(x => x.WhenIMatchThePaths())
|
||||
.And(x => x.ThenTheResultIsTrue())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_match_query_string_with_multiple_params()
|
||||
{
|
||||
const string regExForwardSlashAndOnePlaceHolder = "^(?i)/api/subscriptions/.+/updates\\?unitId=.+&productId=.+$";
|
||||
|
||||
this.Given(x => x.GivenIHaveAUpstreamPath("/api/subscriptions/1/updates?unitId=2"))
|
||||
.And(_ => GivenIHaveAQueryString("?unitId=2&productId=2"))
|
||||
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern(regExForwardSlashAndOnePlaceHolder))
|
||||
.And(_ => GivenThereIsAQueryInTemplate())
|
||||
.When(x => x.WhenIMatchThePaths())
|
||||
.And(x => x.ThenTheResultIsTrue())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_not_match_slash_becaue_we_need_to_match_something_after_it()
|
||||
{
|
||||
const string RegExForwardSlashAndOnePlaceHolder = "^/[0-9a-zA-Z].*";
|
||||
const string regExForwardSlashAndOnePlaceHolder = "^/[0-9a-zA-Z].+";
|
||||
|
||||
this.Given(x => x.GivenIHaveAUpstreamPath("/"))
|
||||
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern(RegExForwardSlashAndOnePlaceHolder))
|
||||
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern(regExForwardSlashAndOnePlaceHolder))
|
||||
.When(x => x.WhenIMatchThePaths())
|
||||
.And(x => x.ThenTheResultIsFalse())
|
||||
.BDDfy();
|
||||
@ -44,7 +87,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
|
||||
public void should_not_match_issue_134()
|
||||
{
|
||||
this.Given(x => x.GivenIHaveAUpstreamPath("/api/vacancy/1/"))
|
||||
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^(?i)/vacancy/.*/$"))
|
||||
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^(?i)/vacancy/.+/$"))
|
||||
.When(x => x.WhenIMatchThePaths())
|
||||
.And(x => x.ThenTheResultIsFalse())
|
||||
.BDDfy();
|
||||
@ -64,7 +107,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
|
||||
public void should_find_match_when_template_smaller_than_valid_path()
|
||||
{
|
||||
this.Given(x => x.GivenIHaveAUpstreamPath("/api/products/2354325435624623464235"))
|
||||
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^/api/products/.*$"))
|
||||
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^/api/products/.+$"))
|
||||
.When(x => x.WhenIMatchThePaths())
|
||||
.And(x => x.ThenTheResultIsTrue())
|
||||
.BDDfy();
|
||||
@ -124,7 +167,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
|
||||
public void can_match_down_stream_url_with_downstream_template_with_one_place_holder()
|
||||
{
|
||||
this.Given(x => x.GivenIHaveAUpstreamPath("api/product/products/1"))
|
||||
.Given(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/product/products/.*$"))
|
||||
.Given(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/product/products/.+$"))
|
||||
.When(x => x.WhenIMatchThePaths())
|
||||
.Then(x => x.ThenTheResultIsTrue())
|
||||
.BDDfy();
|
||||
@ -134,7 +177,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
|
||||
public void can_match_down_stream_url_with_downstream_template_with_two_place_holders()
|
||||
{
|
||||
this.Given(x => x.GivenIHaveAUpstreamPath("api/product/products/1/2"))
|
||||
.Given(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/product/products/.*/.*$"))
|
||||
.Given(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/product/products/.+/.+$"))
|
||||
.When(x => x.WhenIMatchThePaths())
|
||||
.Then(x => x.ThenTheResultIsTrue())
|
||||
.BDDfy();
|
||||
@ -144,7 +187,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
|
||||
public void can_match_down_stream_url_with_downstream_template_with_two_place_holders_seperated_by_something()
|
||||
{
|
||||
this.Given(x => x.GivenIHaveAUpstreamPath("api/product/products/1/categories/2"))
|
||||
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/product/products/.*/categories/.*$"))
|
||||
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/product/products/.+/categories/.+$"))
|
||||
.When(x => x.WhenIMatchThePaths())
|
||||
.Then(x => x.ThenTheResultIsTrue())
|
||||
.BDDfy();
|
||||
@ -154,7 +197,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
|
||||
public void can_match_down_stream_url_with_downstream_template_with_three_place_holders_seperated_by_something()
|
||||
{
|
||||
this.Given(x => x.GivenIHaveAUpstreamPath("api/product/products/1/categories/2/variant/123"))
|
||||
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/product/products/.*/categories/.*/variant/.*$"))
|
||||
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/product/products/.+/categories/.+/variant/.+$"))
|
||||
.When(x => x.WhenIMatchThePaths())
|
||||
.Then(x => x.ThenTheResultIsTrue())
|
||||
.BDDfy();
|
||||
@ -164,7 +207,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
|
||||
public void can_match_down_stream_url_with_downstream_template_with_three_place_holders()
|
||||
{
|
||||
this.Given(x => x.GivenIHaveAUpstreamPath("api/product/products/1/categories/2/variant/"))
|
||||
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/product/products/.*/categories/.*/variant/$"))
|
||||
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/product/products/.+/categories/.+/variant/$"))
|
||||
.When(x => x.WhenIMatchThePaths())
|
||||
.Then(x => x.ThenTheResultIsTrue())
|
||||
.BDDfy();
|
||||
@ -174,7 +217,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
|
||||
public void should_ignore_case_sensitivity()
|
||||
{
|
||||
this.Given(x => x.GivenIHaveAUpstreamPath("API/product/products/1/categories/2/variant/"))
|
||||
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^(?i)api/product/products/.*/categories/.*/variant/$"))
|
||||
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^(?i)api/product/products/.+/categories/.+/variant/$"))
|
||||
.When(x => x.WhenIMatchThePaths())
|
||||
.Then(x => x.ThenTheResultIsTrue())
|
||||
.BDDfy();
|
||||
@ -184,15 +227,20 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
|
||||
public void should_respect_case_sensitivity()
|
||||
{
|
||||
this.Given(x => x.GivenIHaveAUpstreamPath("API/product/products/1/categories/2/variant/"))
|
||||
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/product/products/.*/categories/.*/variant/$"))
|
||||
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/product/products/.+/categories/.+/variant/$"))
|
||||
.When(x => x.WhenIMatchThePaths())
|
||||
.Then(x => x.ThenTheResultIsFalse())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenIHaveAUpstreamPath(string downstreamPath)
|
||||
private void GivenIHaveAUpstreamPath(string path)
|
||||
{
|
||||
_downstreamUrlPath = downstreamPath;
|
||||
_path = path;
|
||||
}
|
||||
|
||||
private void GivenIHaveAQueryString(string queryString)
|
||||
{
|
||||
_queryString = queryString;
|
||||
}
|
||||
|
||||
private void GivenIHaveAnUpstreamUrlTemplatePattern(string downstreamUrlTemplate)
|
||||
@ -202,7 +250,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
|
||||
|
||||
private void WhenIMatchThePaths()
|
||||
{
|
||||
_result = _urlMatcher.Match(_downstreamUrlPath, _downstreamPathTemplate);
|
||||
_result = _urlMatcher.Match(_path, _queryString, _downstreamPathTemplate, _containsQueryString);
|
||||
}
|
||||
|
||||
private void ThenTheResultIsTrue()
|
||||
@ -214,5 +262,10 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
|
||||
{
|
||||
_result.Data.Match.ShouldBeFalse();
|
||||
}
|
||||
|
||||
private void GivenThereIsAQueryInTemplate()
|
||||
{
|
||||
_containsQueryString = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
|
||||
private string _downstreamUrlPath;
|
||||
private string _downstreamPathTemplate;
|
||||
private Response<List<PlaceholderNameAndValue>> _result;
|
||||
private string _query;
|
||||
|
||||
public UrlPathPlaceholderNameAndValueFinderTests()
|
||||
{
|
||||
@ -114,6 +115,91 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_find_query_string()
|
||||
{
|
||||
var expectedTemplates = new List<PlaceholderNameAndValue>
|
||||
{
|
||||
new PlaceholderNameAndValue("{productId}", "1")
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenIHaveAUpstreamPath("/products"))
|
||||
.And(x => x.GivenIHaveAQuery("?productId=1"))
|
||||
.And(x => x.GivenIHaveAnUpstreamUrlTemplate("/products?productId={productId}"))
|
||||
.When(x => x.WhenIFindTheUrlVariableNamesAndValues())
|
||||
.And(x => x.ThenTheTemplatesVariablesAre(expectedTemplates))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_find_query_string_dont_include_hardcoded()
|
||||
{
|
||||
var expectedTemplates = new List<PlaceholderNameAndValue>
|
||||
{
|
||||
new PlaceholderNameAndValue("{productId}", "1")
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenIHaveAUpstreamPath("/products"))
|
||||
.And(x => x.GivenIHaveAQuery("?productId=1&categoryId=2"))
|
||||
.And(x => x.GivenIHaveAnUpstreamUrlTemplate("/products?productId={productId}"))
|
||||
.When(x => x.WhenIFindTheUrlVariableNamesAndValues())
|
||||
.And(x => x.ThenTheTemplatesVariablesAre(expectedTemplates))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_find_multiple_query_string()
|
||||
{
|
||||
var expectedTemplates = new List<PlaceholderNameAndValue>
|
||||
{
|
||||
new PlaceholderNameAndValue("{productId}", "1"),
|
||||
new PlaceholderNameAndValue("{categoryId}", "2")
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenIHaveAUpstreamPath("/products"))
|
||||
.And(x => x.GivenIHaveAQuery("?productId=1&categoryId=2"))
|
||||
.And(x => x.GivenIHaveAnUpstreamUrlTemplate("/products?productId={productId}&categoryId={categoryId}"))
|
||||
.When(x => x.WhenIFindTheUrlVariableNamesAndValues())
|
||||
.And(x => x.ThenTheTemplatesVariablesAre(expectedTemplates))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_find_multiple_query_string_and_path()
|
||||
{
|
||||
var expectedTemplates = new List<PlaceholderNameAndValue>
|
||||
{
|
||||
new PlaceholderNameAndValue("{productId}", "1"),
|
||||
new PlaceholderNameAndValue("{categoryId}", "2"),
|
||||
new PlaceholderNameAndValue("{account}", "3")
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenIHaveAUpstreamPath("/products/3"))
|
||||
.And(x => x.GivenIHaveAQuery("?productId=1&categoryId=2"))
|
||||
.And(x => x.GivenIHaveAnUpstreamUrlTemplate("/products/{account}?productId={productId}&categoryId={categoryId}"))
|
||||
.When(x => x.WhenIFindTheUrlVariableNamesAndValues())
|
||||
.And(x => x.ThenTheTemplatesVariablesAre(expectedTemplates))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_find_multiple_query_string_and_path_that_ends_with_slash()
|
||||
{
|
||||
var expectedTemplates = new List<PlaceholderNameAndValue>
|
||||
{
|
||||
new PlaceholderNameAndValue("{productId}", "1"),
|
||||
new PlaceholderNameAndValue("{categoryId}", "2"),
|
||||
new PlaceholderNameAndValue("{account}", "3")
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenIHaveAUpstreamPath("/products/3/"))
|
||||
.And(x => x.GivenIHaveAQuery("?productId=1&categoryId=2"))
|
||||
.And(x => x.GivenIHaveAnUpstreamUrlTemplate("/products/{account}/?productId={productId}&categoryId={categoryId}"))
|
||||
.When(x => x.WhenIFindTheUrlVariableNamesAndValues())
|
||||
.And(x => x.ThenTheTemplatesVariablesAre(expectedTemplates))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void can_match_down_stream_url_with_no_slash()
|
||||
{
|
||||
@ -260,7 +346,12 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
|
||||
|
||||
private void WhenIFindTheUrlVariableNamesAndValues()
|
||||
{
|
||||
_result = _finder.Find(_downstreamUrlPath, _downstreamPathTemplate);
|
||||
_result = _finder.Find(_downstreamUrlPath, _query, _downstreamPathTemplate);
|
||||
}
|
||||
|
||||
private void GivenIHaveAQuery(string query)
|
||||
{
|
||||
_query = query;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,231 +1,334 @@
|
||||
namespace Ocelot.UnitTests.DownstreamUrlCreator
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Moq;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.DownstreamRouteFinder;
|
||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||
using Ocelot.DownstreamUrlCreator.Middleware;
|
||||
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
using Shouldly;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Request.Middleware;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Middleware;
|
||||
|
||||
public class DownstreamUrlCreatorMiddlewareTests
|
||||
{
|
||||
private readonly Mock<IDownstreamPathPlaceholderReplacer> _downstreamUrlTemplateVariableReplacer;
|
||||
private OkResponse<DownstreamPath> _downstreamPath;
|
||||
private readonly Mock<IOcelotLoggerFactory> _loggerFactory;
|
||||
private Mock<IOcelotLogger> _logger;
|
||||
private DownstreamUrlCreatorMiddleware _middleware;
|
||||
private readonly DownstreamContext _downstreamContext;
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly HttpRequestMessage _request;
|
||||
|
||||
public DownstreamUrlCreatorMiddlewareTests()
|
||||
{
|
||||
_downstreamContext = new DownstreamContext(new DefaultHttpContext());
|
||||
_loggerFactory = new Mock<IOcelotLoggerFactory>();
|
||||
_logger = new Mock<IOcelotLogger>();
|
||||
_loggerFactory.Setup(x => x.CreateLogger<DownstreamUrlCreatorMiddleware>()).Returns(_logger.Object);
|
||||
_downstreamUrlTemplateVariableReplacer = new Mock<IDownstreamPathPlaceholderReplacer>();
|
||||
_request = new HttpRequestMessage(HttpMethod.Get, "https://my.url/abc/?q=123");
|
||||
_downstreamContext.DownstreamRequest = new DownstreamRequest(_request);
|
||||
_next = context => Task.CompletedTask;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_replace_scheme_and_path()
|
||||
{
|
||||
var downstreamReRoute = new DownstreamReRouteBuilder()
|
||||
.WithDownstreamPathTemplate("any old string")
|
||||
.WithUpstreamHttpMethod(new List<string> {"Get"})
|
||||
.WithDownstreamScheme("https")
|
||||
.Build();
|
||||
|
||||
var config = new ServiceProviderConfigurationBuilder()
|
||||
.Build();
|
||||
|
||||
this.Given(x => x.GivenTheDownStreamRouteIs(
|
||||
new DownstreamRoute(
|
||||
new List<PlaceholderNameAndValue>(),
|
||||
new ReRouteBuilder()
|
||||
.WithDownstreamReRoute(downstreamReRoute)
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.Build())))
|
||||
.And(x => x.GivenTheDownstreamRequestUriIs("http://my.url/abc?q=123"))
|
||||
.And(x => GivenTheServiceProviderConfigIs(config))
|
||||
.And(x => x.GivenTheUrlReplacerWillReturn("/api/products/1"))
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
.Then(x => x.ThenTheDownstreamRequestUriIs("https://my.url:80/api/products/1?q=123"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_not_create_service_fabric_url()
|
||||
{
|
||||
var downstreamReRoute = new DownstreamReRouteBuilder()
|
||||
.WithDownstreamPathTemplate("any old string")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithDownstreamScheme("https")
|
||||
.Build();
|
||||
|
||||
var config = new ServiceProviderConfigurationBuilder()
|
||||
.WithType("ServiceFabric")
|
||||
.WithHost("localhost")
|
||||
.WithPort(19081)
|
||||
.Build();
|
||||
|
||||
this.Given(x => x.GivenTheDownStreamRouteIs(
|
||||
new DownstreamRoute(
|
||||
new List<PlaceholderNameAndValue>(),
|
||||
new ReRouteBuilder()
|
||||
.WithDownstreamReRoute(downstreamReRoute)
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.Build())))
|
||||
.And(x => x.GivenTheDownstreamRequestUriIs("http://my.url/abc?q=123"))
|
||||
.And(x => GivenTheServiceProviderConfigIs(config))
|
||||
.And(x => x.GivenTheUrlReplacerWillReturn("/api/products/1"))
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
.Then(x => x.ThenTheDownstreamRequestUriIs("https://my.url:80/api/products/1?q=123"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_create_service_fabric_url()
|
||||
{
|
||||
var downstreamReRoute = new DownstreamReRouteBuilder()
|
||||
.WithDownstreamScheme("http")
|
||||
.WithServiceName("Ocelot/OcelotApp")
|
||||
.WithUseServiceDiscovery(true)
|
||||
.Build();
|
||||
|
||||
var downstreamRoute = new DownstreamRoute(
|
||||
new List<PlaceholderNameAndValue>(),
|
||||
new ReRouteBuilder()
|
||||
.WithDownstreamReRoute(downstreamReRoute)
|
||||
.Build());
|
||||
|
||||
var config = new ServiceProviderConfigurationBuilder()
|
||||
.WithType("ServiceFabric")
|
||||
.WithHost("localhost")
|
||||
.WithPort(19081)
|
||||
.Build();
|
||||
|
||||
this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
|
||||
.And(x => GivenTheServiceProviderConfigIs(config))
|
||||
.And(x => x.GivenTheDownstreamRequestUriIs("http://localhost:19081"))
|
||||
.And(x => x.GivenTheUrlReplacerWillReturn("/api/products/1"))
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
.Then(x => x.ThenTheDownstreamRequestUriIs("http://localhost:19081/Ocelot/OcelotApp/api/products/1?cmd=instance"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_create_service_fabric_url_with_query_string_for_stateless_service()
|
||||
{
|
||||
var downstreamReRoute = new DownstreamReRouteBuilder()
|
||||
.WithDownstreamScheme("http")
|
||||
.WithServiceName("Ocelot/OcelotApp")
|
||||
.WithUseServiceDiscovery(true)
|
||||
.Build();
|
||||
|
||||
var downstreamRoute = new DownstreamRoute(
|
||||
new List<PlaceholderNameAndValue>(),
|
||||
new ReRouteBuilder()
|
||||
.WithDownstreamReRoute(downstreamReRoute)
|
||||
.Build());
|
||||
|
||||
var config = new ServiceProviderConfigurationBuilder()
|
||||
.WithType("ServiceFabric")
|
||||
.WithHost("localhost")
|
||||
.WithPort(19081)
|
||||
.Build();
|
||||
|
||||
this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
|
||||
.And(x => GivenTheServiceProviderConfigIs(config))
|
||||
.And(x => x.GivenTheDownstreamRequestUriIs("http://localhost:19081?Tom=test&laura=1"))
|
||||
.And(x => x.GivenTheUrlReplacerWillReturn("/api/products/1"))
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
.Then(x => x.ThenTheDownstreamRequestUriIs("http://localhost:19081/Ocelot/OcelotApp/api/products/1?Tom=test&laura=1&cmd=instance"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_create_service_fabric_url_with_query_string_for_stateful_service()
|
||||
{
|
||||
var downstreamReRoute = new DownstreamReRouteBuilder()
|
||||
.WithDownstreamScheme("http")
|
||||
.WithServiceName("Ocelot/OcelotApp")
|
||||
.WithUseServiceDiscovery(true)
|
||||
.Build();
|
||||
|
||||
var downstreamRoute = new DownstreamRoute(
|
||||
new List<PlaceholderNameAndValue>(),
|
||||
new ReRouteBuilder()
|
||||
.WithDownstreamReRoute(downstreamReRoute)
|
||||
.Build());
|
||||
|
||||
var config = new ServiceProviderConfigurationBuilder()
|
||||
.WithType("ServiceFabric")
|
||||
.WithHost("localhost")
|
||||
.WithPort(19081)
|
||||
.Build();
|
||||
|
||||
this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
|
||||
.And(x => GivenTheServiceProviderConfigIs(config))
|
||||
.And(x => x.GivenTheDownstreamRequestUriIs("http://localhost:19081?PartitionKind=test&PartitionKey=1"))
|
||||
.And(x => x.GivenTheUrlReplacerWillReturn("/api/products/1"))
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
.Then(x => x.ThenTheDownstreamRequestUriIs("http://localhost:19081/Ocelot/OcelotApp/api/products/1?PartitionKind=test&PartitionKey=1"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenTheServiceProviderConfigIs(ServiceProviderConfiguration config)
|
||||
namespace Ocelot.UnitTests.DownstreamUrlCreator
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Moq;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.DownstreamRouteFinder;
|
||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||
using Ocelot.DownstreamUrlCreator.Middleware;
|
||||
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
using Shouldly;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Request.Middleware;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Middleware;
|
||||
|
||||
public class DownstreamUrlCreatorMiddlewareTests
|
||||
{
|
||||
private readonly Mock<IDownstreamPathPlaceholderReplacer> _downstreamUrlTemplateVariableReplacer;
|
||||
private OkResponse<DownstreamPath> _downstreamPath;
|
||||
private readonly Mock<IOcelotLoggerFactory> _loggerFactory;
|
||||
private Mock<IOcelotLogger> _logger;
|
||||
private DownstreamUrlCreatorMiddleware _middleware;
|
||||
private readonly DownstreamContext _downstreamContext;
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly HttpRequestMessage _request;
|
||||
|
||||
public DownstreamUrlCreatorMiddlewareTests()
|
||||
{
|
||||
var configuration = new InternalConfiguration(null, null, config, null, null, null, null, null);
|
||||
_downstreamContext.Configuration = configuration;
|
||||
}
|
||||
|
||||
private void WhenICallTheMiddleware()
|
||||
{
|
||||
_middleware = new DownstreamUrlCreatorMiddleware(_next, _loggerFactory.Object, _downstreamUrlTemplateVariableReplacer.Object);
|
||||
_middleware.Invoke(_downstreamContext).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute)
|
||||
{
|
||||
_downstreamContext.TemplatePlaceholderNameAndValues = downstreamRoute.TemplatePlaceholderNameAndValues;
|
||||
_downstreamContext.DownstreamReRoute = downstreamRoute.ReRoute.DownstreamReRoute[0];
|
||||
}
|
||||
|
||||
private void GivenTheDownstreamRequestUriIs(string uri)
|
||||
{
|
||||
_request.RequestUri = new Uri(uri);
|
||||
_downstreamContext.DownstreamRequest = new DownstreamRequest(_request);
|
||||
}
|
||||
|
||||
private void GivenTheUrlReplacerWillReturn(string path)
|
||||
{
|
||||
_downstreamPath = new OkResponse<DownstreamPath>(new DownstreamPath(path));
|
||||
_downstreamUrlTemplateVariableReplacer
|
||||
.Setup(x => x.Replace(It.IsAny<PathTemplate>(), It.IsAny<List<PlaceholderNameAndValue>>()))
|
||||
.Returns(_downstreamPath);
|
||||
}
|
||||
|
||||
private void ThenTheDownstreamRequestUriIs(string expectedUri)
|
||||
{
|
||||
_downstreamContext.DownstreamRequest.ToHttpRequestMessage().RequestUri.OriginalString.ShouldBe(expectedUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
_downstreamContext = new DownstreamContext(new DefaultHttpContext());
|
||||
_loggerFactory = new Mock<IOcelotLoggerFactory>();
|
||||
_logger = new Mock<IOcelotLogger>();
|
||||
_loggerFactory.Setup(x => x.CreateLogger<DownstreamUrlCreatorMiddleware>()).Returns(_logger.Object);
|
||||
_downstreamUrlTemplateVariableReplacer = new Mock<IDownstreamPathPlaceholderReplacer>();
|
||||
_request = new HttpRequestMessage(HttpMethod.Get, "https://my.url/abc/?q=123");
|
||||
_downstreamContext.DownstreamRequest = new DownstreamRequest(_request);
|
||||
_next = context => Task.CompletedTask;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_replace_scheme_and_path()
|
||||
{
|
||||
var downstreamReRoute = new DownstreamReRouteBuilder()
|
||||
.WithDownstreamPathTemplate("any old string")
|
||||
.WithUpstreamHttpMethod(new List<string> {"Get"})
|
||||
.WithDownstreamScheme("https")
|
||||
.Build();
|
||||
|
||||
var config = new ServiceProviderConfigurationBuilder()
|
||||
.Build();
|
||||
|
||||
this.Given(x => x.GivenTheDownStreamRouteIs(
|
||||
new DownstreamRoute(
|
||||
new List<PlaceholderNameAndValue>(),
|
||||
new ReRouteBuilder()
|
||||
.WithDownstreamReRoute(downstreamReRoute)
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.Build())))
|
||||
.And(x => x.GivenTheDownstreamRequestUriIs("http://my.url/abc?q=123"))
|
||||
.And(x => GivenTheServiceProviderConfigIs(config))
|
||||
.And(x => x.GivenTheUrlReplacerWillReturn("/api/products/1"))
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
.Then(x => x.ThenTheDownstreamRequestUriIs("https://my.url:80/api/products/1?q=123"))
|
||||
.And(x => ThenTheQueryStringIs("?q=123"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_replace_query_string()
|
||||
{
|
||||
var downstreamReRoute = new DownstreamReRouteBuilder()
|
||||
.WithDownstreamPathTemplate("/api/units/{subscriptionId}/{unitId}/updates")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithDownstreamScheme("https")
|
||||
.Build();
|
||||
|
||||
var config = new ServiceProviderConfigurationBuilder()
|
||||
.Build();
|
||||
|
||||
this.Given(x => x.GivenTheDownStreamRouteIs(
|
||||
new DownstreamRoute(
|
||||
new List<PlaceholderNameAndValue>
|
||||
{
|
||||
new PlaceholderNameAndValue("{subscriptionId}", "1"),
|
||||
new PlaceholderNameAndValue("{unitId}", "2")
|
||||
},
|
||||
new ReRouteBuilder()
|
||||
.WithDownstreamReRoute(downstreamReRoute)
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.Build())))
|
||||
.And(x => x.GivenTheDownstreamRequestUriIs("http://localhost:5000/api/subscriptions/1/updates?unitId=2"))
|
||||
.And(x => GivenTheServiceProviderConfigIs(config))
|
||||
.And(x => x.GivenTheUrlReplacerWillReturn("api/units/1/2/updates"))
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
.Then(x => x.ThenTheDownstreamRequestUriIs("https://localhost:5000/api/units/1/2/updates"))
|
||||
.And(x => ThenTheQueryStringIs(""))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_replace_query_string_but_leave_non_placeholder_queries()
|
||||
{
|
||||
var downstreamReRoute = new DownstreamReRouteBuilder()
|
||||
.WithDownstreamPathTemplate("/api/units/{subscriptionId}/{unitId}/updates")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithDownstreamScheme("https")
|
||||
.Build();
|
||||
|
||||
var config = new ServiceProviderConfigurationBuilder()
|
||||
.Build();
|
||||
|
||||
this.Given(x => x.GivenTheDownStreamRouteIs(
|
||||
new DownstreamRoute(
|
||||
new List<PlaceholderNameAndValue>
|
||||
{
|
||||
new PlaceholderNameAndValue("{subscriptionId}", "1"),
|
||||
new PlaceholderNameAndValue("{unitId}", "2")
|
||||
},
|
||||
new ReRouteBuilder()
|
||||
.WithDownstreamReRoute(downstreamReRoute)
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.Build())))
|
||||
.And(x => x.GivenTheDownstreamRequestUriIs("http://localhost:5000/api/subscriptions/1/updates?unitId=2&productId=2"))
|
||||
.And(x => GivenTheServiceProviderConfigIs(config))
|
||||
.And(x => x.GivenTheUrlReplacerWillReturn("api/units/1/2/updates"))
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
.Then(x => x.ThenTheDownstreamRequestUriIs("https://localhost:5000/api/units/1/2/updates?productId=2"))
|
||||
.And(x => ThenTheQueryStringIs("?productId=2"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_replace_query_string_exact_match()
|
||||
{
|
||||
var downstreamReRoute = new DownstreamReRouteBuilder()
|
||||
.WithDownstreamPathTemplate("/api/units/{subscriptionId}/{unitId}/updates/{unitIdIty}")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithDownstreamScheme("https")
|
||||
.Build();
|
||||
|
||||
var config = new ServiceProviderConfigurationBuilder()
|
||||
.Build();
|
||||
|
||||
this.Given(x => x.GivenTheDownStreamRouteIs(
|
||||
new DownstreamRoute(
|
||||
new List<PlaceholderNameAndValue>
|
||||
{
|
||||
new PlaceholderNameAndValue("{subscriptionId}", "1"),
|
||||
new PlaceholderNameAndValue("{unitId}", "2"),
|
||||
new PlaceholderNameAndValue("{unitIdIty}", "3")
|
||||
},
|
||||
new ReRouteBuilder()
|
||||
.WithDownstreamReRoute(downstreamReRoute)
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.Build())))
|
||||
.And(x => x.GivenTheDownstreamRequestUriIs("http://localhost:5000/api/subscriptions/1/updates?unitId=2?unitIdIty=3"))
|
||||
.And(x => GivenTheServiceProviderConfigIs(config))
|
||||
.And(x => x.GivenTheUrlReplacerWillReturn("api/units/1/2/updates/3"))
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
.Then(x => x.ThenTheDownstreamRequestUriIs("https://localhost:5000/api/units/1/2/updates/3"))
|
||||
.And(x => ThenTheQueryStringIs(""))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_not_create_service_fabric_url()
|
||||
{
|
||||
var downstreamReRoute = new DownstreamReRouteBuilder()
|
||||
.WithDownstreamPathTemplate("any old string")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithDownstreamScheme("https")
|
||||
.Build();
|
||||
|
||||
var config = new ServiceProviderConfigurationBuilder()
|
||||
.WithType("ServiceFabric")
|
||||
.WithHost("localhost")
|
||||
.WithPort(19081)
|
||||
.Build();
|
||||
|
||||
this.Given(x => x.GivenTheDownStreamRouteIs(
|
||||
new DownstreamRoute(
|
||||
new List<PlaceholderNameAndValue>(),
|
||||
new ReRouteBuilder()
|
||||
.WithDownstreamReRoute(downstreamReRoute)
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.Build())))
|
||||
.And(x => x.GivenTheDownstreamRequestUriIs("http://my.url/abc?q=123"))
|
||||
.And(x => GivenTheServiceProviderConfigIs(config))
|
||||
.And(x => x.GivenTheUrlReplacerWillReturn("/api/products/1"))
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
.Then(x => x.ThenTheDownstreamRequestUriIs("https://my.url:80/api/products/1?q=123"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_create_service_fabric_url()
|
||||
{
|
||||
var downstreamReRoute = new DownstreamReRouteBuilder()
|
||||
.WithDownstreamScheme("http")
|
||||
.WithServiceName("Ocelot/OcelotApp")
|
||||
.WithUseServiceDiscovery(true)
|
||||
.Build();
|
||||
|
||||
var downstreamRoute = new DownstreamRoute(
|
||||
new List<PlaceholderNameAndValue>(),
|
||||
new ReRouteBuilder()
|
||||
.WithDownstreamReRoute(downstreamReRoute)
|
||||
.Build());
|
||||
|
||||
var config = new ServiceProviderConfigurationBuilder()
|
||||
.WithType("ServiceFabric")
|
||||
.WithHost("localhost")
|
||||
.WithPort(19081)
|
||||
.Build();
|
||||
|
||||
this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
|
||||
.And(x => GivenTheServiceProviderConfigIs(config))
|
||||
.And(x => x.GivenTheDownstreamRequestUriIs("http://localhost:19081"))
|
||||
.And(x => x.GivenTheUrlReplacerWillReturn("/api/products/1"))
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
.Then(x => x.ThenTheDownstreamRequestUriIs("http://localhost:19081/Ocelot/OcelotApp/api/products/1?cmd=instance"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_create_service_fabric_url_with_query_string_for_stateless_service()
|
||||
{
|
||||
var downstreamReRoute = new DownstreamReRouteBuilder()
|
||||
.WithDownstreamScheme("http")
|
||||
.WithServiceName("Ocelot/OcelotApp")
|
||||
.WithUseServiceDiscovery(true)
|
||||
.Build();
|
||||
|
||||
var downstreamRoute = new DownstreamRoute(
|
||||
new List<PlaceholderNameAndValue>(),
|
||||
new ReRouteBuilder()
|
||||
.WithDownstreamReRoute(downstreamReRoute)
|
||||
.Build());
|
||||
|
||||
var config = new ServiceProviderConfigurationBuilder()
|
||||
.WithType("ServiceFabric")
|
||||
.WithHost("localhost")
|
||||
.WithPort(19081)
|
||||
.Build();
|
||||
|
||||
this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
|
||||
.And(x => GivenTheServiceProviderConfigIs(config))
|
||||
.And(x => x.GivenTheDownstreamRequestUriIs("http://localhost:19081?Tom=test&laura=1"))
|
||||
.And(x => x.GivenTheUrlReplacerWillReturn("/api/products/1"))
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
.Then(x => x.ThenTheDownstreamRequestUriIs("http://localhost:19081/Ocelot/OcelotApp/api/products/1?Tom=test&laura=1&cmd=instance"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_create_service_fabric_url_with_query_string_for_stateful_service()
|
||||
{
|
||||
var downstreamReRoute = new DownstreamReRouteBuilder()
|
||||
.WithDownstreamScheme("http")
|
||||
.WithServiceName("Ocelot/OcelotApp")
|
||||
.WithUseServiceDiscovery(true)
|
||||
.Build();
|
||||
|
||||
var downstreamRoute = new DownstreamRoute(
|
||||
new List<PlaceholderNameAndValue>(),
|
||||
new ReRouteBuilder()
|
||||
.WithDownstreamReRoute(downstreamReRoute)
|
||||
.Build());
|
||||
|
||||
var config = new ServiceProviderConfigurationBuilder()
|
||||
.WithType("ServiceFabric")
|
||||
.WithHost("localhost")
|
||||
.WithPort(19081)
|
||||
.Build();
|
||||
|
||||
this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
|
||||
.And(x => GivenTheServiceProviderConfigIs(config))
|
||||
.And(x => x.GivenTheDownstreamRequestUriIs("http://localhost:19081?PartitionKind=test&PartitionKey=1"))
|
||||
.And(x => x.GivenTheUrlReplacerWillReturn("/api/products/1"))
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
.Then(x => x.ThenTheDownstreamRequestUriIs("http://localhost:19081/Ocelot/OcelotApp/api/products/1?PartitionKind=test&PartitionKey=1"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenTheServiceProviderConfigIs(ServiceProviderConfiguration config)
|
||||
{
|
||||
var configuration = new InternalConfiguration(null, null, config, null, null, null, null, null);
|
||||
_downstreamContext.Configuration = configuration;
|
||||
}
|
||||
|
||||
private void WhenICallTheMiddleware()
|
||||
{
|
||||
_middleware = new DownstreamUrlCreatorMiddleware(_next, _loggerFactory.Object, _downstreamUrlTemplateVariableReplacer.Object);
|
||||
_middleware.Invoke(_downstreamContext).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute)
|
||||
{
|
||||
_downstreamContext.TemplatePlaceholderNameAndValues = downstreamRoute.TemplatePlaceholderNameAndValues;
|
||||
_downstreamContext.DownstreamReRoute = downstreamRoute.ReRoute.DownstreamReRoute[0];
|
||||
}
|
||||
|
||||
private void GivenTheDownstreamRequestUriIs(string uri)
|
||||
{
|
||||
_request.RequestUri = new Uri(uri);
|
||||
_downstreamContext.DownstreamRequest = new DownstreamRequest(_request);
|
||||
}
|
||||
|
||||
private void GivenTheUrlReplacerWillReturn(string path)
|
||||
{
|
||||
_downstreamPath = new OkResponse<DownstreamPath>(new DownstreamPath(path));
|
||||
_downstreamUrlTemplateVariableReplacer
|
||||
.Setup(x => x.Replace(It.IsAny<PathTemplate>(), It.IsAny<List<PlaceholderNameAndValue>>()))
|
||||
.Returns(_downstreamPath);
|
||||
}
|
||||
|
||||
private void ThenTheDownstreamRequestUriIs(string expectedUri)
|
||||
{
|
||||
_downstreamContext.DownstreamRequest.ToHttpRequestMessage().RequestUri.OriginalString.ShouldBe(expectedUri);
|
||||
}
|
||||
|
||||
private void ThenTheQueryStringIs(string queryString)
|
||||
{
|
||||
_downstreamContext.DownstreamRequest.Query.ShouldBe(queryString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,13 @@ namespace Ocelot.UnitTests.Middleware
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Ocelot.DependencyInjection;
|
||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||
using Ocelot.DownstreamUrlCreator.Middleware;
|
||||
using Ocelot.LoadBalancer.Middleware;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Middleware.Pipeline;
|
||||
using Ocelot.Request.Middleware;
|
||||
using Ocelot.WebSockets.Middleware;
|
||||
using Pivotal.Discovery.Client;
|
||||
using Shouldly;
|
||||
using Steeltoe.Common.Discovery;
|
||||
@ -26,6 +31,16 @@ namespace Ocelot.UnitTests.Middleware
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_expand_pipeline()
|
||||
{
|
||||
this.Given(_ => GivenTheDepedenciesAreSetUp())
|
||||
.When(_ => WhenIExpandBuild())
|
||||
.Then(_ => ThenThePipelineIsBuilt())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
|
||||
private void ThenThePipelineIsBuilt()
|
||||
{
|
||||
_handlers.ShouldNotBeNull();
|
||||
@ -36,6 +51,23 @@ namespace Ocelot.UnitTests.Middleware
|
||||
_handlers = _builder.BuildOcelotPipeline(new OcelotPipelineConfiguration());
|
||||
}
|
||||
|
||||
private void WhenIExpandBuild()
|
||||
{
|
||||
OcelotPipelineConfiguration configuration = new OcelotPipelineConfiguration();
|
||||
configuration.MapWhenOcelotPipeline.Add((app) =>
|
||||
{
|
||||
app.UseDownstreamRouteFinderMiddleware();
|
||||
app.UseDownstreamRequestInitialiser();
|
||||
app.UseLoadBalancingMiddleware();
|
||||
app.UseDownstreamUrlCreatorMiddleware();
|
||||
app.UseWebSocketsProxyMiddleware();
|
||||
|
||||
return context => context.HttpContext.WebSockets.IsWebSocketRequest;
|
||||
});
|
||||
_handlers = _builder.BuildOcelotPipeline(new OcelotPipelineConfiguration());
|
||||
}
|
||||
|
||||
|
||||
private void GivenTheDepedenciesAreSetUp()
|
||||
{
|
||||
IConfigurationBuilder test = new ConfigurationBuilder();
|
||||
@ -45,7 +77,6 @@ namespace Ocelot.UnitTests.Middleware
|
||||
services.AddDiscoveryClient(new DiscoveryOptions
|
||||
{
|
||||
ClientType = DiscoveryClientType.EUREKA,
|
||||
//options can not be null
|
||||
ClientOptions = new EurekaClientOptions()
|
||||
{
|
||||
ShouldFetchRegistry = false,
|
||||
|
@ -79,6 +79,8 @@ namespace Ocelot.UnitTests.Middleware
|
||||
del.Invoke(_downstreamContext);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void ThenTheFuncIsInThePipeline()
|
||||
{
|
||||
_counter.ShouldBe(1);
|
||||
|
@ -2,8 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<VersionPrefix>0.0.0-dev</VersionPrefix>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<RuntimeFrameworkVersion>2.0.0</RuntimeFrameworkVersion>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<AssemblyName>Ocelot.UnitTests</AssemblyName>
|
||||
<PackageId>Ocelot.UnitTests</PackageId>
|
||||
<OutputType>Exe</OutputType>
|
||||
@ -38,22 +37,22 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.8" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.0.3" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.2" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
|
||||
<PackageReference Include="Microsoft.DotNet.InternalAbstractions" Version="1.0.500-preview2-1-003177" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.2" />
|
||||
<PackageReference Include="Moq" Version="4.8.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.1.1" />
|
||||
<PackageReference Include="Moq" Version="4.8.3" />
|
||||
<PackageReference Include="Shouldly" Version="3.0.0" />
|
||||
<PackageReference Include="TestStack.BDDfy" Version="4.3.2" />
|
||||
<PackageReference Include="xunit" Version="2.3.1" />
|
||||
|
@ -23,15 +23,18 @@ namespace Ocelot.UnitTests.Requester
|
||||
{
|
||||
public class HttpClientBuilderTests : IDisposable
|
||||
{
|
||||
private readonly HttpClientBuilder _builder;
|
||||
private HttpClientBuilder _builder;
|
||||
private readonly Mock<IDelegatingHandlerHandlerFactory> _factory;
|
||||
private IHttpClient _httpClient;
|
||||
private HttpResponseMessage _response;
|
||||
private DownstreamContext _context;
|
||||
private readonly Mock<IHttpClientCache> _cacheHandlers;
|
||||
private Mock<IOcelotLogger> _logger;
|
||||
private readonly Mock<IOcelotLogger> _logger;
|
||||
private int _count;
|
||||
private IWebHost _host;
|
||||
private IHttpClient _againHttpClient;
|
||||
private IHttpClient _firstHttpClient;
|
||||
private MemoryHttpClientCache _realCache;
|
||||
|
||||
public HttpClientBuilderTests()
|
||||
{
|
||||
@ -61,6 +64,47 @@ namespace Ocelot.UnitTests.Requester
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_get_from_cache()
|
||||
{
|
||||
var qosOptions = new QoSOptionsBuilder()
|
||||
.Build();
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true))
|
||||
.WithLoadBalancerKey("")
|
||||
.WithQosOptions(new QoSOptionsBuilder().Build())
|
||||
.Build();
|
||||
|
||||
this.Given(x => GivenARealCache())
|
||||
.And(x => GivenTheFactoryReturns())
|
||||
.And(x => GivenARequest(reRoute))
|
||||
.And(x => WhenIBuildTheFirstTime())
|
||||
.And(x => WhenISave())
|
||||
.And(x => WhenIBuildAgain())
|
||||
.And(x => WhenISave())
|
||||
.When(x => WhenIBuildAgain())
|
||||
.Then(x => ThenTheHttpClientIsFromTheCache())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenARealCache()
|
||||
{
|
||||
_realCache = new MemoryHttpClientCache();
|
||||
_builder = new HttpClientBuilder(_factory.Object, _realCache, _logger.Object);
|
||||
}
|
||||
|
||||
private void ThenTheHttpClientIsFromTheCache()
|
||||
{
|
||||
_againHttpClient.ShouldBe(_firstHttpClient);
|
||||
}
|
||||
|
||||
private void WhenISave()
|
||||
{
|
||||
_builder.Save();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_log_if_ignoring_ssl_errors()
|
||||
{
|
||||
@ -302,6 +346,17 @@ namespace Ocelot.UnitTests.Requester
|
||||
_httpClient = _builder.Create(_context);
|
||||
}
|
||||
|
||||
private void WhenIBuildTheFirstTime()
|
||||
{
|
||||
_firstHttpClient = _builder.Create(_context);
|
||||
}
|
||||
|
||||
private void WhenIBuildAgain()
|
||||
{
|
||||
_builder = new HttpClientBuilder(_factory.Object, _realCache, _logger.Object);
|
||||
_againHttpClient = _builder.Create(_context);
|
||||
}
|
||||
|
||||
private void ThenTheHttpClientShouldNotBeNull()
|
||||
{
|
||||
_httpClient.ShouldNotBeNull();
|
||||
|
@ -1,33 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Consul;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Moq;
|
||||
using Ocelot.Infrastructure.Consul;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.ServiceDiscovery.Configuration;
|
||||
using Ocelot.ServiceDiscovery.Providers;
|
||||
using Ocelot.Values;
|
||||
using Xunit;
|
||||
using TestStack.BDDfy;
|
||||
using Shouldly;
|
||||
using static Ocelot.Infrastructure.Wait;
|
||||
|
||||
namespace Ocelot.UnitTests.ServiceDiscovery
|
||||
namespace Ocelot.UnitTests.ServiceDiscovery
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Moq;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.ServiceDiscovery.Providers;
|
||||
using Ocelot.Values;
|
||||
using Xunit;
|
||||
using TestStack.BDDfy;
|
||||
using Shouldly;
|
||||
using static Ocelot.Infrastructure.Wait;
|
||||
|
||||
public class PollingConsulServiceDiscoveryProviderTests
|
||||
{
|
||||
private readonly int _delay;
|
||||
private PollingConsulServiceDiscoveryProvider _provider;
|
||||
private readonly string _serviceName;
|
||||
private List<Service> _services;
|
||||
private readonly List<Service> _services;
|
||||
private readonly Mock<IOcelotLoggerFactory> _factory;
|
||||
private readonly Mock<IOcelotLogger> _logger;
|
||||
private Mock<IServiceDiscoveryProvider> _consulServiceDiscoveryProvider;
|
||||
private readonly Mock<IServiceDiscoveryProvider> _consulServiceDiscoveryProvider;
|
||||
private List<Service> _result;
|
||||
|
||||
public PollingConsulServiceDiscoveryProviderTests()
|
||||
@ -64,7 +55,7 @@ namespace Ocelot.UnitTests.ServiceDiscovery
|
||||
|
||||
private void WhenIGetTheServices(int expected)
|
||||
{
|
||||
_provider = new PollingConsulServiceDiscoveryProvider(_delay, _serviceName, _factory.Object, _consulServiceDiscoveryProvider.Object);
|
||||
_provider = new PollingConsulServiceDiscoveryProvider(_delay, "", _factory.Object, _consulServiceDiscoveryProvider.Object);
|
||||
|
||||
var result = WaitFor(3000).Until(() => {
|
||||
try
|
||||
|
Loading…
x
Reference in New Issue
Block a user