Merge pull request #48 from ThreeMammals/develop

merge newest code
This commit is contained in:
geffzhang 2018-07-20 07:17:13 +08:00 committed by GitHub
commit 2fded67de8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
68 changed files with 3046 additions and 2017 deletions

View File

@ -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

View File

@ -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.

View File

@ -100,7 +100,7 @@ In order to use IdentityServer bearer tokens register your IdentityServer servic
public void ConfigureServices(IServiceCollection services)
{
var authenticationProviderKey = "TestKey";
var options = o =>
Action<IdentityServerAuthenticationOptions> options = o =>
{
o.Authority = "https://whereyouridentityserverlives.com";
o.ApiName = "api";
@ -136,6 +136,11 @@ Then map the authentication provider key to a ReRoute in your configuration e.g.
}
}]
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
^^^^^^^^^^^^^

View File

@ -2,15 +2,19 @@ Caching
=======
Ocelot supports some very rudimentary caching at the moment provider by
the `CacheManager <http://cachemanager.net/>`_ project. This is an amazing project
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. 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.
cache with Ocelot.
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.
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.
@ -19,3 +23,12 @@ In order to use caching on a route in your ReRoute configuration add this settin
"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.

View File

@ -1,5 +1,5 @@
Delegating Handers
==================
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>`_.
@ -23,21 +23,13 @@ asp.net core container so you can inject any other services you have registered
}
}
Next you must add the handlers to Ocelot's container either as singleton like follows..
Next you must add the handlers to Ocelot's container like below...
.. code-block:: csharp
services.AddOcelot()
.AddSingletonDelegatingHandler<FakeHandler>()
.AddSingletonDelegatingHandler<FakeHandlerTwo>()
Or transient as below...
.. code-block:: csharp
services.AddOcelot()
.AddTransientDelegatingHandler<FakeHandler>()
.AddTransientDelegatingHandler<FakeHandlerTwo>()
.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
@ -45,17 +37,12 @@ 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...
As below...
.. code-block:: csharp
services.AddOcelot()
.AddTransientDelegatingHandler<FakeHandler>(true)
.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

View File

@ -185,3 +185,63 @@ 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.

View File

@ -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
},

View File

@ -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**

View File

@ -1,6 +1,6 @@
{
"projects": [ "src", "test" ],
"sdk": {
"version": "2.1.4"
"version": "2.1.301"
}
}

View File

@ -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)

View File

@ -16,6 +16,7 @@ namespace Ocelot.Configuration.Creator
{
var upstreamTemplate = reRoute.UpstreamPathTemplate;
var placeholders = new List<string>();
for (var i = 0; i < upstreamTemplate.Length; i++)
@ -30,11 +31,19 @@ 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)
{
upstreamTemplate = upstreamTemplate.Replace(placeholder, RegExMatchOneOrMoreOfEverything);
@ -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)

View File

@ -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;
}

View File

@ -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>()

View File

@ -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)

View File

@ -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);

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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)
{

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -5,13 +5,20 @@ 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);
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));
}
}
}

View File

@ -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;
}

View File

@ -1,17 +1,15 @@
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
{
using System.Text.RegularExpressions;
public class DownstreamUrlCreatorMiddleware : OcelotMiddleware
{
private readonly OcelotRequestDelegate _next;
@ -28,14 +26,14 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
public async Task Invoke(DownstreamContext context)
{
var dsPath = _replacer
var response = _replacer
.Replace(context.DownstreamReRoute.DownstreamPathTemplate, context.TemplatePlaceholderNameAndValues);
if (dsPath.IsError)
if (response.IsError)
{
Logger.LogDebug("IDownstreamPathPlaceholderReplacer returned an error, setting pipeline error");
SetPipelineError(context, dsPath.Errors);
SetPipelineError(context, response.Errors);
return;
}
@ -43,13 +41,25 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
if (ServiceFabricRequest(context))
{
var pathAndQuery = CreateServiceFabricUri(context, dsPath);
var pathAndQuery = CreateServiceFabricUri(context, response);
context.DownstreamRequest.AbsolutePath = pathAndQuery.path;
context.DownstreamRequest.Query = pathAndQuery.query;
}
else
{
context.DownstreamRequest.AbsolutePath = dsPath.Data.Value;
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}");
@ -57,6 +67,44 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
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;

View File

@ -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);

View File

@ -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"));

View File

@ -29,13 +29,19 @@
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);
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;
}
@ -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 =>
{

View File

@ -1,6 +1,8 @@
namespace Ocelot.Middleware
{
using Ocelot.Middleware.Pipeline;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public class OcelotPipelineConfiguration
@ -38,5 +40,9 @@
/// This allows the user to implement there own query string manipulation logic
/// </summary>
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>>>();
}
}

View File

@ -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);

View File

@ -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 =>

View File

@ -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>

View File

@ -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();
}
}

View File

@ -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 ||

View File

@ -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);

View File

@ -40,6 +40,7 @@ namespace Ocelot.Requester
if (httpClient != null)
{
_client = httpClient;
return httpClient;
}

View File

@ -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);
}
}

View File

@ -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
namespace Ocelot.Requester
{
using System;
using System.Collections.Concurrent;
public class MemoryHttpClientCache : IHttpClientCache
{
private readonly ConcurrentDictionary<string, ConcurrentQueue<IHttpClient>> _httpClientsCache = new ConcurrentDictionary<string, ConcurrentQueue<IHttpClient>>();
private readonly ConcurrentDictionary<string, IHttpClient> _httpClientsCache;
public void Set(string id, IHttpClient client, TimeSpan expirationTime)
public MemoryHttpClientCache()
{
ConcurrentQueue<IHttpClient> connectionQueue;
if (_httpClientsCache.TryGetValue(id, out connectionQueue))
{
connectionQueue.Enqueue(client);
}
else
{
connectionQueue = new ConcurrentQueue<IHttpClient>();
connectionQueue.Enqueue(client);
_httpClientsCache.TryAdd(id, connectionQueue);
}
_httpClientsCache = new ConcurrentDictionary<string, IHttpClient>();
}
public bool Exists(string id)
public void Set(string key, IHttpClient client, TimeSpan expirationTime)
{
ConcurrentQueue<IHttpClient> connectionQueue;
return _httpClientsCache.TryGetValue(id, out connectionQueue);
_httpClientsCache.AddOrUpdate(key, client, (k, oldValue) => client);
}
public IHttpClient Get(string id)
public IHttpClient Get(string key)
{
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);
//todo handle error?
return _httpClientsCache.TryGetValue(key, out var client) ? client : null;
}
}
}

View File

@ -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 bool ContainsQueryString { get; }
}
}

View File

@ -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)
{

View File

@ -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" />

View 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();
}
}
}

View 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();
}
}
}

View File

@ -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 =>
{

View File

@ -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>

View File

@ -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 *

View File

@ -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>

View File

@ -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>

View File

@ -7,6 +7,11 @@
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
{
@ -33,10 +38,11 @@
});
s.AddOcelot()
.AddCacheManager(x =>
{
x.WithDictionaryHandle();
})
.AddDelegatingHandler<FakeHandler>(true)
// .AddCacheManager(x =>
// {
// x.WithDictionaryHandle();
// })
/*.AddOpenTracing(option =>
{
option.CollectorUrl = "http://localhost:9618";
@ -58,4 +64,15 @@
.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);
}
}
}

View File

@ -16,21 +16,32 @@
}
},
{
"DownstreamPathTemplate": "/api/values",
"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": "localhost",
"Port": 5007
}
],
"HttpHandlerOptions": {
"AllowAutoRedirect": true,
"UseCookieContainer": true,
"UseTracing": true
"Host": "testapivalues.azurewebsites.net",
"Port": 443
}
]
},
{
"DownstreamPathTemplate": "/",
@ -331,4 +342,4 @@
"GlobalConfiguration": {
"RequestIdKey": "ot-traceid"
}
}
}

View File

@ -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()

View File

@ -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;

View File

@ -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));

View File

@ -52,17 +52,6 @@ namespace Ocelot.UnitTests.DependencyInjection
.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()
{
@ -74,17 +63,6 @@ namespace Ocelot.UnitTests.DependencyInjection
.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()
{
@ -231,15 +209,6 @@ namespace Ocelot.UnitTests.DependencyInjection
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();
@ -262,13 +231,13 @@ namespace Ocelot.UnitTests.DependencyInjection
private void AddTransientGlobalDelegatingHandler<T>()
where T : DelegatingHandler
{
_ocelotBuilder.AddTransientDelegatingHandler<T>(true);
_ocelotBuilder.AddDelegatingHandler<T>(true);
}
private void AddSpecificTransientDelegatingHandler<T>()
where T : DelegatingHandler
{
_ocelotBuilder.AddTransientDelegatingHandler<T>();
_ocelotBuilder.AddDelegatingHandler<T>();
}
private void ThenTheCorrectAdminPathIsRegitered()
@ -365,13 +334,13 @@ namespace Ocelot.UnitTests.DependencyInjection
private void AddGlobalDelegatingHandler<T>()
where T : DelegatingHandler
{
_ocelotBuilder.AddSingletonDelegatingHandler<T>(true);
_ocelotBuilder.AddDelegatingHandler<T>(true);
}
private void AddSpecificDelegatingHandler<T>()
where T : DelegatingHandler
{
_ocelotBuilder.AddSingletonDelegatingHandler<T>();
_ocelotBuilder.AddDelegatingHandler<T>();
}
private void ThenAnOcelotBuilderIsReturned()

View File

@ -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()

View File

@ -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);
}

View File

@ -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)

View File

@ -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>())

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -68,6 +68,104 @@
.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();
}
@ -227,5 +325,10 @@
{
_downstreamContext.DownstreamRequest.ToHttpRequestMessage().RequestUri.OriginalString.ShouldBe(expectedUri);
}
private void ThenTheQueryStringIs(string queryString)
{
_downstreamContext.DownstreamRequest.Query.ShouldBe(queryString);
}
}
}

View File

@ -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,

View File

@ -79,6 +79,8 @@ namespace Ocelot.UnitTests.Middleware
del.Invoke(_downstreamContext);
}
private void ThenTheFuncIsInThePipeline()
{
_counter.ShouldBe(1);

View File

@ -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" />

View File

@ -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();

View File

@ -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