Changed routing to support a catch all style (#187)

* Changed routing to support a catch all style

* refactoring placeholder tuff

* implemented simple priority in the routing
This commit is contained in:
Tom Pallister
2018-01-05 21:26:15 +00:00
committed by GitHub
parent 931a115ffa
commit fef19ddf98
34 changed files with 575 additions and 165 deletions

View File

@ -2,6 +2,7 @@
using System.Net.Http;
using Ocelot.Values;
using System.Linq;
using Ocelot.Configuration.Creator;
namespace Ocelot.Configuration.Builder
{
@ -11,7 +12,7 @@ namespace Ocelot.Configuration.Builder
private string _loadBalancerKey;
private string _downstreamPathTemplate;
private string _upstreamTemplate;
private string _upstreamTemplatePattern;
private UpstreamPathTemplate _upstreamTemplatePattern;
private List<HttpMethod> _upstreamHttpMethod;
private bool _isAuthenticated;
private List<ClaimToThing> _configHeaderExtractorProperties;
@ -65,7 +66,7 @@ namespace Ocelot.Configuration.Builder
return this;
}
public ReRouteBuilder WithUpstreamTemplatePattern(string input)
public ReRouteBuilder WithUpstreamTemplatePattern(UpstreamPathTemplate input)
{
_upstreamTemplatePattern = input;
return this;

View File

@ -1,9 +1,10 @@
using Ocelot.Configuration.File;
using Ocelot.Values;
namespace Ocelot.Configuration.Creator
{
public interface IUpstreamTemplatePatternCreator
{
string Create(FileReRoute reRoute);
UpstreamPathTemplate Create(FileReRoute reRoute);
}
}

View File

@ -1,5 +1,6 @@
using System.Collections.Generic;
using Ocelot.Configuration.File;
using Ocelot.Values;
namespace Ocelot.Configuration.Creator
{
@ -11,7 +12,7 @@ namespace Ocelot.Configuration.Creator
private const string RegExForwardSlashOnly = "^/$";
private const string RegExForwardSlashAndOnePlaceHolder = "^/.*";
public string Create(FileReRoute reRoute)
public UpstreamPathTemplate Create(FileReRoute reRoute)
{
var upstreamTemplate = reRoute.UpstreamPathTemplate;
@ -26,9 +27,10 @@ namespace Ocelot.Configuration.Creator
var placeHolderName = upstreamTemplate.Substring(i, difference);
placeholders.Add(placeHolderName);
//hack to handle /{url} case
if(ForwardSlashAndOnePlaceHolder(upstreamTemplate, placeholders, postitionOfPlaceHolderClosingBracket))
{
return RegExForwardSlashAndOnePlaceHolder;
return new UpstreamPathTemplate(RegExForwardSlashAndOnePlaceHolder, 0);
}
}
}
@ -40,7 +42,7 @@ namespace Ocelot.Configuration.Creator
if (upstreamTemplate == "/")
{
return RegExForwardSlashOnly;
return new UpstreamPathTemplate(RegExForwardSlashOnly, 1);
}
if(upstreamTemplate.EndsWith("/"))
@ -52,7 +54,7 @@ namespace Ocelot.Configuration.Creator
? $"^{upstreamTemplate}{RegExMatchEndString}"
: $"^{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}";
return route;
return new UpstreamPathTemplate(route, 1);
}
private bool ForwardSlashAndOnePlaceHolder(string upstreamTemplate, List<string> placeholders, int postitionOfPlaceHolderClosingBracket)

View File

@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Net.Http;
using Ocelot.Configuration.Creator;
using Ocelot.Values;
namespace Ocelot.Configuration
@ -9,7 +10,7 @@ namespace Ocelot.Configuration
public ReRoute(PathTemplate downstreamPathTemplate,
PathTemplate upstreamPathTemplate,
List<HttpMethod> upstreamHttpMethod,
string upstreamTemplatePattern,
UpstreamPathTemplate upstreamTemplatePattern,
bool isAuthenticated,
AuthenticationOptions authenticationOptions,
List<ClaimToThing> claimsToHeaders,
@ -67,7 +68,7 @@ namespace Ocelot.Configuration
public string ReRouteKey {get;private set;}
public PathTemplate DownstreamPathTemplate { get; private set; }
public PathTemplate UpstreamPathTemplate { get; private set; }
public string UpstreamTemplatePattern { get; private set; }
public UpstreamPathTemplate UpstreamTemplatePattern { get; private set; }
public List<HttpMethod> UpstreamHttpMethod { get; private set; }
public bool IsAuthenticated { get; private set; }
public bool IsAuthorised { get; private set; }

View File

@ -108,7 +108,7 @@ namespace Ocelot.DependencyInjection
_services.TryAddSingleton<IAddQueriesToRequest, AddQueriesToRequest>();
_services.TryAddSingleton<IClaimsParser, ClaimsParser>();
_services.TryAddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>();
_services.TryAddSingleton<IUrlPathPlaceholderNameAndValueFinder, UrlPathPlaceholderNameAndValueFinder>();
_services.TryAddSingleton<IPlaceholderNameAndValueFinder, UrlPathPlaceholderNameAndValueFinder>();
_services.TryAddSingleton<IDownstreamPathPlaceholderReplacer, DownstreamTemplatePathPlaceholderReplacer>();
_services.TryAddSingleton<IDownstreamRouteFinder, DownstreamRouteFinder.Finder.DownstreamRouteFinder>();
_services.TryAddSingleton<IHttpRequester, HttpClientHttpRequester>();

View File

@ -6,12 +6,12 @@ namespace Ocelot.DownstreamRouteFinder
{
public class DownstreamRoute
{
public DownstreamRoute(List<UrlPathPlaceholderNameAndValue> templatePlaceholderNameAndValues, ReRoute reRoute)
public DownstreamRoute(List<PlaceholderNameAndValue> templatePlaceholderNameAndValues, ReRoute reRoute)
{
TemplatePlaceholderNameAndValues = templatePlaceholderNameAndValues;
ReRoute = reRoute;
}
public List<UrlPathPlaceholderNameAndValue> TemplatePlaceholderNameAndValues { get; private set; }
public List<PlaceholderNameAndValue> TemplatePlaceholderNameAndValues { get; private set; }
public ReRoute ReRoute { get; private set; }
}
}

View File

@ -13,34 +13,30 @@ namespace Ocelot.DownstreamRouteFinder.Finder
public class DownstreamRouteFinder : IDownstreamRouteFinder
{
private readonly IUrlPathToUrlTemplateMatcher _urlMatcher;
private readonly IUrlPathPlaceholderNameAndValueFinder _urlPathPlaceholderNameAndValueFinder;
private readonly IPlaceholderNameAndValueFinder __placeholderNameAndValueFinder;
public DownstreamRouteFinder(IUrlPathToUrlTemplateMatcher urlMatcher, IUrlPathPlaceholderNameAndValueFinder urlPathPlaceholderNameAndValueFinder)
public DownstreamRouteFinder(IUrlPathToUrlTemplateMatcher urlMatcher, IPlaceholderNameAndValueFinder urlPathPlaceholderNameAndValueFinder)
{
_urlMatcher = urlMatcher;
_urlPathPlaceholderNameAndValueFinder = urlPathPlaceholderNameAndValueFinder;
__placeholderNameAndValueFinder = urlPathPlaceholderNameAndValueFinder;
}
public Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod, IOcelotConfiguration configuration)
public Response<DownstreamRoute> FindDownstreamRoute(string path, string httpMethod, IOcelotConfiguration configuration)
{
var applicableReRoutes = configuration.ReRoutes.Where(r => r.UpstreamHttpMethod.Count == 0 || r.UpstreamHttpMethod.Select(x => x.Method.ToLower()).Contains(upstreamHttpMethod.ToLower()));
var applicableReRoutes = configuration.ReRoutes.Where(r => r.UpstreamHttpMethod.Count == 0 || r.UpstreamHttpMethod.Select(x => x.Method.ToLower()).Contains(httpMethod.ToLower())).OrderByDescending(x => x.UpstreamTemplatePattern.Priority);
foreach (var reRoute in applicableReRoutes)
{
if (upstreamUrlPath == reRoute.UpstreamTemplatePattern)
if (path == reRoute.UpstreamTemplatePattern.Template)
{
var templateVariableNameAndValues = _urlPathPlaceholderNameAndValueFinder.Find(upstreamUrlPath, reRoute.UpstreamPathTemplate.Value);
return new OkResponse<DownstreamRoute>(new DownstreamRoute(templateVariableNameAndValues.Data, reRoute));
return GetPlaceholderNamesAndValues(path, reRoute);
}
var urlMatch = _urlMatcher.Match(upstreamUrlPath, reRoute.UpstreamTemplatePattern);
var urlMatch = _urlMatcher.Match(path, reRoute.UpstreamTemplatePattern.Template);
if (urlMatch.Data.Match)
{
var templateVariableNameAndValues = _urlPathPlaceholderNameAndValueFinder.Find(upstreamUrlPath, reRoute.UpstreamPathTemplate.Value);
return new OkResponse<DownstreamRoute>(new DownstreamRoute(templateVariableNameAndValues.Data, reRoute));
return GetPlaceholderNamesAndValues(path, reRoute);
}
}
@ -49,5 +45,12 @@ namespace Ocelot.DownstreamRouteFinder.Finder
new UnableToFindDownstreamRouteError()
});
}
private OkResponse<DownstreamRoute> GetPlaceholderNamesAndValues(string path, ReRoute reRoute)
{
var templatePlaceholderNameAndValues = __placeholderNameAndValueFinder.Find(path, reRoute.UpstreamPathTemplate.Value);
return new OkResponse<DownstreamRoute>(new DownstreamRoute(templatePlaceholderNameAndValues.Data, reRoute));
}
}
}

View File

@ -3,8 +3,8 @@ using Ocelot.Responses;
namespace Ocelot.DownstreamRouteFinder.UrlMatcher
{
public interface IUrlPathPlaceholderNameAndValueFinder
public interface IPlaceholderNameAndValueFinder
{
Response<List<UrlPathPlaceholderNameAndValue>> Find(string upstreamUrlPath, string upstreamUrlPathTemplate);
Response<List<PlaceholderNameAndValue>> Find(string path, string pathTemplate);
}
}

View File

@ -1,13 +1,13 @@
namespace Ocelot.DownstreamRouteFinder.UrlMatcher
{
public class UrlPathPlaceholderNameAndValue
public class PlaceholderNameAndValue
{
public UrlPathPlaceholderNameAndValue(string templateVariableName, string templateVariableValue)
public PlaceholderNameAndValue(string name, string value)
{
TemplateVariableName = templateVariableName;
TemplateVariableValue = templateVariableValue;
Name = name;
Value = value;
}
public string TemplateVariableName {get;private set;}
public string TemplateVariableValue {get;private set;}
public string Name {get;private set;}
public string Value {get;private set;}
}
}

View File

@ -3,44 +3,72 @@ using Ocelot.Responses;
namespace Ocelot.DownstreamRouteFinder.UrlMatcher
{
public class UrlPathPlaceholderNameAndValueFinder : IUrlPathPlaceholderNameAndValueFinder
public class UrlPathPlaceholderNameAndValueFinder : IPlaceholderNameAndValueFinder
{
public Response<List<UrlPathPlaceholderNameAndValue>> Find(string upstreamUrlPath, string upstreamUrlPathTemplate)
public Response<List<PlaceholderNameAndValue>> Find(string path, string pathTemplate)
{
var templateKeysAndValues = new List<UrlPathPlaceholderNameAndValue>();
var placeHolderNameAndValues = new List<PlaceholderNameAndValue>();
int counterForUrl = 0;
int counterForPath = 0;
for (int counterForTemplate = 0; counterForTemplate < upstreamUrlPathTemplate.Length; counterForTemplate++)
for (int counterForTemplate = 0; counterForTemplate < pathTemplate.Length; counterForTemplate++)
{
if ((upstreamUrlPath.Length > counterForUrl) && CharactersDontMatch(upstreamUrlPathTemplate[counterForTemplate], upstreamUrlPath[counterForUrl]) && ContinueScanningUrl(counterForUrl,upstreamUrlPath.Length))
if ((path.Length > counterForPath) && CharactersDontMatch(pathTemplate[counterForTemplate], path[counterForPath]) && ContinueScanningUrl(counterForPath,path.Length))
{
if (IsPlaceholder(upstreamUrlPathTemplate[counterForTemplate]))
if (IsPlaceholder(pathTemplate[counterForTemplate]))
{
var variableName = GetPlaceholderVariableName(upstreamUrlPathTemplate, counterForTemplate);
var placeholderName = GetPlaceholderName(pathTemplate, counterForTemplate);
var variableValue = GetPlaceholderVariableValue(upstreamUrlPathTemplate, variableName, upstreamUrlPath, counterForUrl);
var placeholderValue = GetPlaceholderValue(pathTemplate, placeholderName, path, counterForPath);
var templateVariableNameAndValue = new UrlPathPlaceholderNameAndValue(variableName, variableValue);
placeHolderNameAndValues.Add(new PlaceholderNameAndValue(placeholderName, placeholderValue));
templateKeysAndValues.Add(templateVariableNameAndValue);
counterForTemplate = GetNextCounterPosition(pathTemplate, counterForTemplate, '}');
counterForTemplate = GetNextCounterPosition(upstreamUrlPathTemplate, counterForTemplate, '}');
counterForUrl = GetNextCounterPosition(upstreamUrlPath, counterForUrl, '/');
counterForPath = GetNextCounterPosition(path, counterForPath, '/');
continue;
}
return new OkResponse<List<UrlPathPlaceholderNameAndValue>>(templateKeysAndValues);
return new OkResponse<List<PlaceholderNameAndValue>>(placeHolderNameAndValues);
}
counterForUrl++;
else if(IsCatchAll(path, counterForPath, pathTemplate))
{
var endOfPlaceholder = GetNextCounterPosition(pathTemplate, counterForTemplate, '}');
var placeholderName = GetPlaceholderName(pathTemplate, 1);
if(NothingAfterFirstForwardSlash(path))
{
placeHolderNameAndValues.Add(new PlaceholderNameAndValue(placeholderName, ""));
}
else
{
var placeholderValue = GetPlaceholderValue(pathTemplate, placeholderName, path, counterForPath + 1);
placeHolderNameAndValues.Add(new PlaceholderNameAndValue(placeholderName, placeholderValue));
}
counterForTemplate = endOfPlaceholder;
}
counterForPath++;
}
return new OkResponse<List<UrlPathPlaceholderNameAndValue>>(templateKeysAndValues);
return new OkResponse<List<PlaceholderNameAndValue>>(placeHolderNameAndValues);
}
private string GetPlaceholderVariableValue(string urlPathTemplate, string variableName, string urlPath, int counterForUrl)
private bool IsCatchAll(string path, int counterForPath, string pathTemplate)
{
return string.IsNullOrEmpty(path) || (path.Length > counterForPath && path[counterForPath] == '/') && pathTemplate.Length > 1
&& pathTemplate.Substring(0, 2) == "/{"
&& pathTemplate.IndexOf('}') == pathTemplate.Length - 1;
}
private bool NothingAfterFirstForwardSlash(string path)
{
return path.Length == 1 || path.Length == 0;
}
private string GetPlaceholderValue(string urlPathTemplate, string variableName, string urlPath, int counterForUrl)
{
var positionOfNextSlash = urlPath.IndexOf('/', counterForUrl);
@ -54,7 +82,7 @@ namespace Ocelot.DownstreamRouteFinder.UrlMatcher
return variableValue;
}
private string GetPlaceholderVariableName(string urlPathTemplate, int counterForTemplate)
private string GetPlaceholderName(string urlPathTemplate, int counterForTemplate)
{
var postitionOfPlaceHolderClosingBracket = urlPathTemplate.IndexOf('}', counterForTemplate) + 1;

View File

@ -8,7 +8,7 @@ namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer
{
public class DownstreamTemplatePathPlaceholderReplacer : IDownstreamPathPlaceholderReplacer
{
public Response<DownstreamPath> Replace(PathTemplate downstreamPathTemplate, List<UrlPathPlaceholderNameAndValue> urlPathPlaceholderNameAndValues)
public Response<DownstreamPath> Replace(PathTemplate downstreamPathTemplate, List<PlaceholderNameAndValue> urlPathPlaceholderNameAndValues)
{
var downstreamPath = new StringBuilder();
@ -16,7 +16,7 @@ namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer
foreach (var placeholderVariableAndValue in urlPathPlaceholderNameAndValues)
{
downstreamPath.Replace(placeholderVariableAndValue.TemplateVariableName, placeholderVariableAndValue.TemplateVariableValue);
downstreamPath.Replace(placeholderVariableAndValue.Name, placeholderVariableAndValue.Value);
}
return new OkResponse<DownstreamPath>(new DownstreamPath(downstreamPath.ToString()));

View File

@ -7,6 +7,6 @@ namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer
{
public interface IDownstreamPathPlaceholderReplacer
{
Response<DownstreamPath> Replace(PathTemplate downstreamPathTemplate, List<UrlPathPlaceholderNameAndValue> urlPathPlaceholderNameAndValues);
Response<DownstreamPath> Replace(PathTemplate downstreamPathTemplate, List<PlaceholderNameAndValue> urlPathPlaceholderNameAndValues);
}
}

View File

@ -0,0 +1,14 @@
namespace Ocelot.Values
{
public class UpstreamPathTemplate
{
public UpstreamPathTemplate(string template, int priority)
{
Template = template;
Priority = priority;
}
public string Template {get;}
public int Priority {get;}
}
}