Feature/issue 209 upstream host based routing (#216)

* from messing around at lunch...initial hacking seems to sort of work..need to think of all the test scenarios

* drunken train hacking

* docs for upstreamhost
This commit is contained in:
Tom Pallister
2018-02-02 11:06:54 +00:00
committed by GitHub
parent 86951d6aaa
commit 5848e12d15
13 changed files with 581 additions and 41 deletions

View File

@ -10,7 +10,7 @@ namespace Ocelot.Configuration.Builder
public class ReRouteBuilder
{
private AuthenticationOptions _authenticationOptions;
private string _loadBalancerKey;
private string _reRouteKey;
private string _downstreamPathTemplate;
private string _upstreamTemplate;
private UpstreamPathTemplate _upstreamTemplatePattern;
@ -36,6 +36,7 @@ namespace Ocelot.Configuration.Builder
private List<HeaderFindAndReplace> _upstreamHeaderFindAndReplace;
private List<HeaderFindAndReplace> _downstreamHeaderFindAndReplace;
private readonly List<DownstreamHostAndPort> _downstreamAddresses;
private string _upstreamHost;
public ReRouteBuilder()
{
@ -48,6 +49,12 @@ namespace Ocelot.Configuration.Builder
return this;
}
public ReRouteBuilder WithUpstreamHost(string upstreamAddresses)
{
_upstreamHost = upstreamAddresses;
return this;
}
public ReRouteBuilder WithLoadBalancer(string loadBalancer)
{
_loadBalancer = loadBalancer;
@ -150,9 +157,9 @@ namespace Ocelot.Configuration.Builder
return this;
}
public ReRouteBuilder WithReRouteKey(string loadBalancerKey)
public ReRouteBuilder WithReRouteKey(string reRouteKey)
{
_loadBalancerKey = loadBalancerKey;
_reRouteKey = reRouteKey;
return this;
}
@ -204,6 +211,7 @@ namespace Ocelot.Configuration.Builder
return this;
}
public ReRoute Build()
{
return new ReRoute(
@ -223,7 +231,7 @@ namespace Ocelot.Configuration.Builder
_fileCacheOptions,
_downstreamScheme,
_loadBalancer,
_loadBalancerKey,
_reRouteKey,
_useQos,
_qosOptions,
_enableRateLimiting,
@ -233,7 +241,8 @@ namespace Ocelot.Configuration.Builder
_serviceName,
_upstreamHeaderFindAndReplace,
_downstreamHeaderFindAndReplace,
_downstreamAddresses);
_downstreamAddresses,
_upstreamHost);
}
}
}

View File

@ -166,6 +166,7 @@ namespace Ocelot.Configuration.Creator
.WithUseServiceDiscovery(fileReRoute.UseServiceDiscovery)
.WithUpstreamHeaderFindAndReplace(hAndRs.Upstream)
.WithDownstreamHeaderFindAndReplace(hAndRs.Downstream)
.WithUpstreamHost(fileReRoute.UpstreamHost)
.Build();
return reRoute;

View File

@ -42,5 +42,6 @@ namespace Ocelot.Configuration.File
public FileHttpHandlerOptions HttpHandlerOptions { get; set; }
public bool UseServiceDiscovery {get;set;}
public List<FileHostAndPort> DownstreamHostAndPorts {get;set;}
public string UpstreamHost { get; set; }
}
}

View File

@ -33,15 +33,17 @@ namespace Ocelot.Configuration
string serviceName,
List<HeaderFindAndReplace> upstreamHeadersFindAndReplace,
List<HeaderFindAndReplace> downstreamHeadersFindAndReplace,
List<DownstreamHostAndPort> downstreamAddresses)
List<DownstreamHostAndPort> downstreamAddresses,
string upstreamHost)
{
DownstreamHeadersFindAndReplace = downstreamHeadersFindAndReplace;
UpstreamHeadersFindAndReplace = upstreamHeadersFindAndReplace;
UpstreamHost = upstreamHost;
DownstreamHeadersFindAndReplace = downstreamHeadersFindAndReplace ?? new List<HeaderFindAndReplace>();
UpstreamHeadersFindAndReplace = upstreamHeadersFindAndReplace ?? new List<HeaderFindAndReplace>();
ServiceName = serviceName;
UseServiceDiscovery = useServiceDiscovery;
ReRouteKey = reRouteKey;
LoadBalancer = loadBalancer;
DownstreamAddresses = downstreamAddresses;
DownstreamAddresses = downstreamAddresses ?? new List<DownstreamHostAndPort>();
DownstreamPathTemplate = downstreamPathTemplate;
UpstreamPathTemplate = upstreamPathTemplate;
UpstreamHttpMethod = upstreamHttpMethod;
@ -53,12 +55,9 @@ namespace Ocelot.Configuration
RequestIdKey = requestIdKey;
IsCached = isCached;
CacheOptions = cacheOptions;
ClaimsToQueries = claimsToQueries
?? new List<ClaimToThing>();
ClaimsToClaims = claimsToClaims
?? new List<ClaimToThing>();
ClaimsToHeaders = claimsToHeaders
?? new List<ClaimToThing>();
ClaimsToQueries = claimsToQueries ?? new List<ClaimToThing>();
ClaimsToClaims = claimsToClaims ?? new List<ClaimToThing>();
ClaimsToHeaders = claimsToHeaders ?? new List<ClaimToThing>();
DownstreamScheme = downstreamScheme;
IsQos = isQos;
QosOptionsOptions = qosOptions;
@ -94,6 +93,6 @@ namespace Ocelot.Configuration
public List<HeaderFindAndReplace> UpstreamHeadersFindAndReplace {get;private set;}
public List<HeaderFindAndReplace> DownstreamHeadersFindAndReplace {get;private set;}
public List<DownstreamHostAndPort> DownstreamAddresses {get;private set;}
public string UpstreamHost { get; private set; }
}
}

View File

@ -39,7 +39,8 @@ namespace Ocelot.Configuration.Validator
private static bool IsNotDuplicateIn(FileReRoute reRoute, List<FileReRoute> reRoutes)
{
var matchingReRoutes = reRoutes.Where(r => r.UpstreamPathTemplate == reRoute.UpstreamPathTemplate).ToList();
var matchingReRoutes = reRoutes
.Where(r => r.UpstreamPathTemplate == reRoute.UpstreamPathTemplate && (r.UpstreamHost != reRoute.UpstreamHost || reRoute.UpstreamHost == null)).ToList();
if(matchingReRoutes.Count == 1)
{

View File

@ -13,44 +13,56 @@ namespace Ocelot.DownstreamRouteFinder.Finder
public class DownstreamRouteFinder : IDownstreamRouteFinder
{
private readonly IUrlPathToUrlTemplateMatcher _urlMatcher;
private readonly IPlaceholderNameAndValueFinder __placeholderNameAndValueFinder;
private readonly IPlaceholderNameAndValueFinder _placeholderNameAndValueFinder;
public DownstreamRouteFinder(IUrlPathToUrlTemplateMatcher urlMatcher, IPlaceholderNameAndValueFinder urlPathPlaceholderNameAndValueFinder)
{
_urlMatcher = urlMatcher;
__placeholderNameAndValueFinder = urlPathPlaceholderNameAndValueFinder;
_placeholderNameAndValueFinder = urlPathPlaceholderNameAndValueFinder;
}
public Response<DownstreamRoute> FindDownstreamRoute(string path, string httpMethod, IOcelotConfiguration configuration)
public Response<DownstreamRoute> FindDownstreamRoute(string path, string httpMethod, IOcelotConfiguration configuration, string upstreamHost)
{
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);
var downstreamRoutes = new List<DownstreamRoute>();
var applicableReRoutes = configuration.ReRoutes
.Where(r => RouteIsApplicableToThisRequest(r, httpMethod, upstreamHost))
.OrderByDescending(x => x.UpstreamTemplatePattern.Priority);
foreach (var reRoute in applicableReRoutes)
{
if (path == reRoute.UpstreamTemplatePattern.Template)
{
return GetPlaceholderNamesAndValues(path, reRoute);
}
var urlMatch = _urlMatcher.Match(path, reRoute.UpstreamTemplatePattern.Template);
if (urlMatch.Data.Match)
{
return GetPlaceholderNamesAndValues(path, reRoute);
downstreamRoutes.Add(GetPlaceholderNamesAndValues(path, reRoute));
}
}
if (downstreamRoutes.Any())
{
var notNullOption = downstreamRoutes.FirstOrDefault(x => !string.IsNullOrEmpty(x.ReRoute.UpstreamHost));
var nullOption = downstreamRoutes.FirstOrDefault(x => string.IsNullOrEmpty(x.ReRoute.UpstreamHost));
return notNullOption != null ? new OkResponse<DownstreamRoute>(notNullOption) : new OkResponse<DownstreamRoute>(nullOption);
}
return new ErrorResponse<DownstreamRoute>(new List<Error>
{
new UnableToFindDownstreamRouteError()
});
}
private OkResponse<DownstreamRoute> GetPlaceholderNamesAndValues(string path, ReRoute reRoute)
private bool RouteIsApplicableToThisRequest(ReRoute reRoute, string httpMethod, string upstreamHost)
{
var templatePlaceholderNameAndValues = __placeholderNameAndValueFinder.Find(path, reRoute.UpstreamPathTemplate.Value);
return reRoute.UpstreamHttpMethod.Count == 0 || reRoute.UpstreamHttpMethod.Select(x => x.Method.ToLower()).Contains(httpMethod.ToLower()) && !(!string.IsNullOrEmpty(reRoute.UpstreamHost) && reRoute.UpstreamHost != upstreamHost);
}
return new OkResponse<DownstreamRoute>(new DownstreamRoute(templatePlaceholderNameAndValues.Data, reRoute));
private DownstreamRoute GetPlaceholderNamesAndValues(string path, ReRoute reRoute)
{
var templatePlaceholderNameAndValues = _placeholderNameAndValueFinder.Find(path, reRoute.UpstreamPathTemplate.Value);
return new DownstreamRoute(templatePlaceholderNameAndValues.Data, reRoute);
}
}
}
}

View File

@ -6,6 +6,6 @@ namespace Ocelot.DownstreamRouteFinder.Finder
{
public interface IDownstreamRouteFinder
{
Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod, IOcelotConfiguration configuration);
Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod, IOcelotConfiguration configuration, string upstreamHost);
}
}

View File

@ -36,6 +36,8 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
{
var upstreamUrlPath = context.Request.Path.ToString();
var upstreamHost = context.Request.Headers["Host"];
var configuration = await _configProvider.Get();
if(configuration.IsError)
@ -49,7 +51,7 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
_logger.LogDebug("upstream url path is {upstreamUrlPath}", upstreamUrlPath);
var downstreamRoute = _downstreamRouteFinder.FindDownstreamRoute(upstreamUrlPath, context.Request.Method, configuration.Data);
var downstreamRoute = _downstreamRouteFinder.FindDownstreamRoute(upstreamUrlPath, context.Request.Method, configuration.Data, upstreamHost);
if (downstreamRoute.IsError)
{
@ -66,4 +68,4 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
await _next.Invoke(context);
}
}
}
}