mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 06:22:50 +08:00
Merge branch 'release-3.0.1'
This commit is contained in:
commit
deac86fb8a
@ -3,7 +3,7 @@ Routing
|
|||||||
|
|
||||||
Ocelot's primary functionality is to take incomeing http requests and forward them on
|
Ocelot's primary functionality is to take incomeing http requests and forward them on
|
||||||
to a downstream service. At the moment in the form of another http request (in the future
|
to a downstream service. At the moment in the form of another http request (in the future
|
||||||
this could be any transport mechanism.).
|
this could be any transport mechanism).
|
||||||
|
|
||||||
Ocelot's describes the routing of one request to another as a ReRoute. In order to get
|
Ocelot's describes the routing of one request to another as a ReRoute. In order to get
|
||||||
anything working in Ocelot you need to set up a ReRoute in the configuration.
|
anything working in Ocelot you need to set up a ReRoute in the configuration.
|
||||||
@ -110,3 +110,33 @@ The catch all has a lower priority than any other ReRoute. If you also have the
|
|||||||
"UpstreamPathTemplate": "/",
|
"UpstreamPathTemplate": "/",
|
||||||
"UpstreamHttpMethod": [ "Get" ]
|
"UpstreamHttpMethod": [ "Get" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Upstream Host
|
||||||
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
This feature allows you to have ReRoutes based on the upstream host. This works by looking at the host header the client has used and then using this as part of the information we use to identify a ReRoute.
|
||||||
|
|
||||||
|
In order to use this feature please add the following to your config.
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/",
|
||||||
|
"DownstreamScheme": "https",
|
||||||
|
"DownstreamHostAndPorts": [
|
||||||
|
{
|
||||||
|
"Host": "10.0.10.1",
|
||||||
|
"Port": 80,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"UpstreamPathTemplate": "/",
|
||||||
|
"UpstreamHttpMethod": [ "Get" ],
|
||||||
|
"UpstreamHost": "somedomain.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
The ReRoute above will only be matched when the host header value is somedomain.com.
|
||||||
|
|
||||||
|
If you do not set UpstreamHost on a ReRoue then any host header can match it. This is basically a catch all and
|
||||||
|
preservers existing functionality at the time of building the feature. This means that if you have two ReRoutes that are the same apart from the UpstreamHost where one is null and the other set. Ocelot will favour the one that has been set.
|
||||||
|
|
||||||
|
This feature was requested as part of `Issue 216 <https://github.com/TomPallister/Ocelot/pull/216>`_ .
|
@ -10,7 +10,7 @@ namespace Ocelot.Configuration.Builder
|
|||||||
public class ReRouteBuilder
|
public class ReRouteBuilder
|
||||||
{
|
{
|
||||||
private AuthenticationOptions _authenticationOptions;
|
private AuthenticationOptions _authenticationOptions;
|
||||||
private string _loadBalancerKey;
|
private string _reRouteKey;
|
||||||
private string _downstreamPathTemplate;
|
private string _downstreamPathTemplate;
|
||||||
private string _upstreamTemplate;
|
private string _upstreamTemplate;
|
||||||
private UpstreamPathTemplate _upstreamTemplatePattern;
|
private UpstreamPathTemplate _upstreamTemplatePattern;
|
||||||
@ -36,6 +36,7 @@ namespace Ocelot.Configuration.Builder
|
|||||||
private List<HeaderFindAndReplace> _upstreamHeaderFindAndReplace;
|
private List<HeaderFindAndReplace> _upstreamHeaderFindAndReplace;
|
||||||
private List<HeaderFindAndReplace> _downstreamHeaderFindAndReplace;
|
private List<HeaderFindAndReplace> _downstreamHeaderFindAndReplace;
|
||||||
private readonly List<DownstreamHostAndPort> _downstreamAddresses;
|
private readonly List<DownstreamHostAndPort> _downstreamAddresses;
|
||||||
|
private string _upstreamHost;
|
||||||
|
|
||||||
public ReRouteBuilder()
|
public ReRouteBuilder()
|
||||||
{
|
{
|
||||||
@ -48,6 +49,12 @@ namespace Ocelot.Configuration.Builder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ReRouteBuilder WithUpstreamHost(string upstreamAddresses)
|
||||||
|
{
|
||||||
|
_upstreamHost = upstreamAddresses;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public ReRouteBuilder WithLoadBalancer(string loadBalancer)
|
public ReRouteBuilder WithLoadBalancer(string loadBalancer)
|
||||||
{
|
{
|
||||||
_loadBalancer = loadBalancer;
|
_loadBalancer = loadBalancer;
|
||||||
@ -150,9 +157,9 @@ namespace Ocelot.Configuration.Builder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReRouteBuilder WithReRouteKey(string loadBalancerKey)
|
public ReRouteBuilder WithReRouteKey(string reRouteKey)
|
||||||
{
|
{
|
||||||
_loadBalancerKey = loadBalancerKey;
|
_reRouteKey = reRouteKey;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,6 +211,7 @@ namespace Ocelot.Configuration.Builder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public ReRoute Build()
|
public ReRoute Build()
|
||||||
{
|
{
|
||||||
return new ReRoute(
|
return new ReRoute(
|
||||||
@ -223,7 +231,7 @@ namespace Ocelot.Configuration.Builder
|
|||||||
_fileCacheOptions,
|
_fileCacheOptions,
|
||||||
_downstreamScheme,
|
_downstreamScheme,
|
||||||
_loadBalancer,
|
_loadBalancer,
|
||||||
_loadBalancerKey,
|
_reRouteKey,
|
||||||
_useQos,
|
_useQos,
|
||||||
_qosOptions,
|
_qosOptions,
|
||||||
_enableRateLimiting,
|
_enableRateLimiting,
|
||||||
@ -233,7 +241,8 @@ namespace Ocelot.Configuration.Builder
|
|||||||
_serviceName,
|
_serviceName,
|
||||||
_upstreamHeaderFindAndReplace,
|
_upstreamHeaderFindAndReplace,
|
||||||
_downstreamHeaderFindAndReplace,
|
_downstreamHeaderFindAndReplace,
|
||||||
_downstreamAddresses);
|
_downstreamAddresses,
|
||||||
|
_upstreamHost);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,6 +166,7 @@ namespace Ocelot.Configuration.Creator
|
|||||||
.WithUseServiceDiscovery(fileReRoute.UseServiceDiscovery)
|
.WithUseServiceDiscovery(fileReRoute.UseServiceDiscovery)
|
||||||
.WithUpstreamHeaderFindAndReplace(hAndRs.Upstream)
|
.WithUpstreamHeaderFindAndReplace(hAndRs.Upstream)
|
||||||
.WithDownstreamHeaderFindAndReplace(hAndRs.Downstream)
|
.WithDownstreamHeaderFindAndReplace(hAndRs.Downstream)
|
||||||
|
.WithUpstreamHost(fileReRoute.UpstreamHost)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
return reRoute;
|
return reRoute;
|
||||||
|
@ -42,5 +42,6 @@ namespace Ocelot.Configuration.File
|
|||||||
public FileHttpHandlerOptions HttpHandlerOptions { get; set; }
|
public FileHttpHandlerOptions HttpHandlerOptions { get; set; }
|
||||||
public bool UseServiceDiscovery {get;set;}
|
public bool UseServiceDiscovery {get;set;}
|
||||||
public List<FileHostAndPort> DownstreamHostAndPorts {get;set;}
|
public List<FileHostAndPort> DownstreamHostAndPorts {get;set;}
|
||||||
|
public string UpstreamHost { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,15 +33,17 @@ namespace Ocelot.Configuration
|
|||||||
string serviceName,
|
string serviceName,
|
||||||
List<HeaderFindAndReplace> upstreamHeadersFindAndReplace,
|
List<HeaderFindAndReplace> upstreamHeadersFindAndReplace,
|
||||||
List<HeaderFindAndReplace> downstreamHeadersFindAndReplace,
|
List<HeaderFindAndReplace> downstreamHeadersFindAndReplace,
|
||||||
List<DownstreamHostAndPort> downstreamAddresses)
|
List<DownstreamHostAndPort> downstreamAddresses,
|
||||||
|
string upstreamHost)
|
||||||
{
|
{
|
||||||
DownstreamHeadersFindAndReplace = downstreamHeadersFindAndReplace;
|
UpstreamHost = upstreamHost;
|
||||||
UpstreamHeadersFindAndReplace = upstreamHeadersFindAndReplace;
|
DownstreamHeadersFindAndReplace = downstreamHeadersFindAndReplace ?? new List<HeaderFindAndReplace>();
|
||||||
|
UpstreamHeadersFindAndReplace = upstreamHeadersFindAndReplace ?? new List<HeaderFindAndReplace>();
|
||||||
ServiceName = serviceName;
|
ServiceName = serviceName;
|
||||||
UseServiceDiscovery = useServiceDiscovery;
|
UseServiceDiscovery = useServiceDiscovery;
|
||||||
ReRouteKey = reRouteKey;
|
ReRouteKey = reRouteKey;
|
||||||
LoadBalancer = loadBalancer;
|
LoadBalancer = loadBalancer;
|
||||||
DownstreamAddresses = downstreamAddresses;
|
DownstreamAddresses = downstreamAddresses ?? new List<DownstreamHostAndPort>();
|
||||||
DownstreamPathTemplate = downstreamPathTemplate;
|
DownstreamPathTemplate = downstreamPathTemplate;
|
||||||
UpstreamPathTemplate = upstreamPathTemplate;
|
UpstreamPathTemplate = upstreamPathTemplate;
|
||||||
UpstreamHttpMethod = upstreamHttpMethod;
|
UpstreamHttpMethod = upstreamHttpMethod;
|
||||||
@ -53,12 +55,9 @@ namespace Ocelot.Configuration
|
|||||||
RequestIdKey = requestIdKey;
|
RequestIdKey = requestIdKey;
|
||||||
IsCached = isCached;
|
IsCached = isCached;
|
||||||
CacheOptions = cacheOptions;
|
CacheOptions = cacheOptions;
|
||||||
ClaimsToQueries = claimsToQueries
|
ClaimsToQueries = claimsToQueries ?? new List<ClaimToThing>();
|
||||||
?? new List<ClaimToThing>();
|
ClaimsToClaims = claimsToClaims ?? new List<ClaimToThing>();
|
||||||
ClaimsToClaims = claimsToClaims
|
ClaimsToHeaders = claimsToHeaders ?? new List<ClaimToThing>();
|
||||||
?? new List<ClaimToThing>();
|
|
||||||
ClaimsToHeaders = claimsToHeaders
|
|
||||||
?? new List<ClaimToThing>();
|
|
||||||
DownstreamScheme = downstreamScheme;
|
DownstreamScheme = downstreamScheme;
|
||||||
IsQos = isQos;
|
IsQos = isQos;
|
||||||
QosOptionsOptions = qosOptions;
|
QosOptionsOptions = qosOptions;
|
||||||
@ -94,6 +93,6 @@ namespace Ocelot.Configuration
|
|||||||
public List<HeaderFindAndReplace> UpstreamHeadersFindAndReplace {get;private set;}
|
public List<HeaderFindAndReplace> UpstreamHeadersFindAndReplace {get;private set;}
|
||||||
public List<HeaderFindAndReplace> DownstreamHeadersFindAndReplace {get;private set;}
|
public List<HeaderFindAndReplace> DownstreamHeadersFindAndReplace {get;private set;}
|
||||||
public List<DownstreamHostAndPort> DownstreamAddresses {get;private set;}
|
public List<DownstreamHostAndPort> DownstreamAddresses {get;private set;}
|
||||||
|
public string UpstreamHost { get; private set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,8 @@ namespace Ocelot.Configuration.Validator
|
|||||||
|
|
||||||
private static bool IsNotDuplicateIn(FileReRoute reRoute, List<FileReRoute> reRoutes)
|
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)
|
if(matchingReRoutes.Count == 1)
|
||||||
{
|
{
|
||||||
|
@ -13,44 +13,56 @@ namespace Ocelot.DownstreamRouteFinder.Finder
|
|||||||
public class DownstreamRouteFinder : IDownstreamRouteFinder
|
public class DownstreamRouteFinder : IDownstreamRouteFinder
|
||||||
{
|
{
|
||||||
private readonly IUrlPathToUrlTemplateMatcher _urlMatcher;
|
private readonly IUrlPathToUrlTemplateMatcher _urlMatcher;
|
||||||
private readonly IPlaceholderNameAndValueFinder __placeholderNameAndValueFinder;
|
private readonly IPlaceholderNameAndValueFinder _placeholderNameAndValueFinder;
|
||||||
|
|
||||||
public DownstreamRouteFinder(IUrlPathToUrlTemplateMatcher urlMatcher, IPlaceholderNameAndValueFinder urlPathPlaceholderNameAndValueFinder)
|
public DownstreamRouteFinder(IUrlPathToUrlTemplateMatcher urlMatcher, IPlaceholderNameAndValueFinder urlPathPlaceholderNameAndValueFinder)
|
||||||
{
|
{
|
||||||
_urlMatcher = urlMatcher;
|
_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)
|
foreach (var reRoute in applicableReRoutes)
|
||||||
{
|
{
|
||||||
if (path == reRoute.UpstreamTemplatePattern.Template)
|
|
||||||
{
|
|
||||||
return GetPlaceholderNamesAndValues(path, reRoute);
|
|
||||||
}
|
|
||||||
|
|
||||||
var urlMatch = _urlMatcher.Match(path, reRoute.UpstreamTemplatePattern.Template);
|
var urlMatch = _urlMatcher.Match(path, reRoute.UpstreamTemplatePattern.Template);
|
||||||
|
|
||||||
if (urlMatch.Data.Match)
|
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>
|
return new ErrorResponse<DownstreamRoute>(new List<Error>
|
||||||
{
|
{
|
||||||
new UnableToFindDownstreamRouteError()
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,6 +6,6 @@ namespace Ocelot.DownstreamRouteFinder.Finder
|
|||||||
{
|
{
|
||||||
public interface IDownstreamRouteFinder
|
public interface IDownstreamRouteFinder
|
||||||
{
|
{
|
||||||
Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod, IOcelotConfiguration configuration);
|
Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod, IOcelotConfiguration configuration, string upstreamHost);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,8 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
|
|||||||
{
|
{
|
||||||
var upstreamUrlPath = context.Request.Path.ToString();
|
var upstreamUrlPath = context.Request.Path.ToString();
|
||||||
|
|
||||||
|
var upstreamHost = context.Request.Headers["Host"];
|
||||||
|
|
||||||
var configuration = await _configProvider.Get();
|
var configuration = await _configProvider.Get();
|
||||||
|
|
||||||
if(configuration.IsError)
|
if(configuration.IsError)
|
||||||
@ -49,7 +51,7 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
|
|||||||
|
|
||||||
_logger.LogDebug("upstream url path is {upstreamUrlPath}", upstreamUrlPath);
|
_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)
|
if (downstreamRoute.IsError)
|
||||||
{
|
{
|
||||||
|
290
test/Ocelot.AcceptanceTests/UpstreamHostTests.cs
Normal file
290
test/Ocelot.AcceptanceTests/UpstreamHostTests.cs
Normal file
@ -0,0 +1,290 @@
|
|||||||
|
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 UpstreamHostTests : IDisposable
|
||||||
|
{
|
||||||
|
private IWebHost _builder;
|
||||||
|
private readonly Steps _steps;
|
||||||
|
private string _downstreamPath;
|
||||||
|
|
||||||
|
public UpstreamHostTests()
|
||||||
|
{
|
||||||
|
_steps = new Steps();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_response_200_with_simple_url_and_hosts_match()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 51879,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpstreamPathTemplate = "/",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
UpstreamHost = "localhost"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Hello from Laura"))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_response_200_with_simple_url_and_hosts_match_multiple_re_routes()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 51879,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpstreamPathTemplate = "/",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
UpstreamHost = "localhost"
|
||||||
|
},
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 50000,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpstreamPathTemplate = "/",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
UpstreamHost = "DONTMATCH"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Hello from Laura"))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_response_200_with_simple_url_and_hosts_match_multiple_re_routes_reversed()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 50000,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpstreamPathTemplate = "/",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
UpstreamHost = "DONTMATCH"
|
||||||
|
},
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 51879,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpstreamPathTemplate = "/",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
UpstreamHost = "localhost"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Hello from Laura"))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_response_200_with_simple_url_and_hosts_match_multiple_re_routes_reversed_with_no_host_first()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 50000,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpstreamPathTemplate = "/",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
},
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 51879,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpstreamPathTemplate = "/",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
UpstreamHost = "localhost"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Hello from Laura"))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_response_404_with_simple_url_and_hosts_dont_match()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 51879,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpstreamPathTemplate = "/",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
UpstreamHost = "127.0.0.20:5000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Hello from Laura"))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
|
||||||
|
{
|
||||||
|
_builder = new WebHostBuilder()
|
||||||
|
.UseUrls(baseUrl)
|
||||||
|
.UseKestrel()
|
||||||
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
|
.UseIISIntegration()
|
||||||
|
.Configure(app =>
|
||||||
|
{
|
||||||
|
app.UsePathBase(basePath);
|
||||||
|
app.Run(async context =>
|
||||||
|
{
|
||||||
|
_downstreamPath = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
|
||||||
|
|
||||||
|
if(_downstreamPath != basePath)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = statusCode;
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,8 +3,12 @@
|
|||||||
{
|
{
|
||||||
"DownstreamPathTemplate": "/",
|
"DownstreamPathTemplate": "/",
|
||||||
"DownstreamScheme": "http",
|
"DownstreamScheme": "http",
|
||||||
"DownstreamHost": "localhost",
|
"DownstreamHostAndPorts": [
|
||||||
"DownstreamPort": 52876,
|
{
|
||||||
|
"Host": "localhost",
|
||||||
|
"Port": 52876
|
||||||
|
}
|
||||||
|
],
|
||||||
"UpstreamPathTemplate": "/identityserverexample",
|
"UpstreamPathTemplate": "/identityserverexample",
|
||||||
"UpstreamHttpMethod": [ "Get" ],
|
"UpstreamHttpMethod": [ "Get" ],
|
||||||
"QoSOptions": {
|
"QoSOptions": {
|
||||||
@ -45,8 +49,12 @@
|
|||||||
{
|
{
|
||||||
"DownstreamPathTemplate": "/posts",
|
"DownstreamPathTemplate": "/posts",
|
||||||
"DownstreamScheme": "https",
|
"DownstreamScheme": "https",
|
||||||
"DownstreamHost": "jsonplaceholder.typicode.com",
|
"DownstreamHostAndPorts": [
|
||||||
"DownstreamPort": 443,
|
{
|
||||||
|
"Host": "jsonplaceholder.typicode.com",
|
||||||
|
"Port": 443
|
||||||
|
}
|
||||||
|
],
|
||||||
"UpstreamPathTemplate": "/posts",
|
"UpstreamPathTemplate": "/posts",
|
||||||
"UpstreamHttpMethod": [ "Get" ],
|
"UpstreamHttpMethod": [ "Get" ],
|
||||||
"QoSOptions": {
|
"QoSOptions": {
|
||||||
@ -58,8 +66,12 @@
|
|||||||
{
|
{
|
||||||
"DownstreamPathTemplate": "/posts/{postId}",
|
"DownstreamPathTemplate": "/posts/{postId}",
|
||||||
"DownstreamScheme": "http",
|
"DownstreamScheme": "http",
|
||||||
"DownstreamHost": "jsonplaceholder.typicode.com",
|
"DownstreamHostAndPorts": [
|
||||||
"DownstreamPort": 80,
|
{
|
||||||
|
"Host": "jsonplaceholder.typicode.com",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
"UpstreamPathTemplate": "/posts/{postId}",
|
"UpstreamPathTemplate": "/posts/{postId}",
|
||||||
"UpstreamHttpMethod": [ "Get" ],
|
"UpstreamHttpMethod": [ "Get" ],
|
||||||
"RequestIdKey": "ReRouteRequestId",
|
"RequestIdKey": "ReRouteRequestId",
|
||||||
@ -72,8 +84,12 @@
|
|||||||
{
|
{
|
||||||
"DownstreamPathTemplate": "/posts/{postId}/comments",
|
"DownstreamPathTemplate": "/posts/{postId}/comments",
|
||||||
"DownstreamScheme": "http",
|
"DownstreamScheme": "http",
|
||||||
"DownstreamHost": "jsonplaceholder.typicode.com",
|
"DownstreamHostAndPorts": [
|
||||||
"DownstreamPort": 80,
|
{
|
||||||
|
"Host": "jsonplaceholder.typicode.com",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
"UpstreamPathTemplate": "/posts/{postId}/comments",
|
"UpstreamPathTemplate": "/posts/{postId}/comments",
|
||||||
"UpstreamHttpMethod": [ "Get" ],
|
"UpstreamHttpMethod": [ "Get" ],
|
||||||
"QoSOptions": {
|
"QoSOptions": {
|
||||||
@ -85,8 +101,12 @@
|
|||||||
{
|
{
|
||||||
"DownstreamPathTemplate": "/comments",
|
"DownstreamPathTemplate": "/comments",
|
||||||
"DownstreamScheme": "http",
|
"DownstreamScheme": "http",
|
||||||
"DownstreamHost": "jsonplaceholder.typicode.com",
|
"DownstreamHostAndPorts": [
|
||||||
"DownstreamPort": 80,
|
{
|
||||||
|
"Host": "jsonplaceholder.typicode.com",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
"UpstreamPathTemplate": "/comments",
|
"UpstreamPathTemplate": "/comments",
|
||||||
"UpstreamHttpMethod": [ "Get" ],
|
"UpstreamHttpMethod": [ "Get" ],
|
||||||
"QoSOptions": {
|
"QoSOptions": {
|
||||||
@ -98,8 +118,12 @@
|
|||||||
{
|
{
|
||||||
"DownstreamPathTemplate": "/posts",
|
"DownstreamPathTemplate": "/posts",
|
||||||
"DownstreamScheme": "http",
|
"DownstreamScheme": "http",
|
||||||
"DownstreamHost": "jsonplaceholder.typicode.com",
|
"DownstreamHostAndPorts": [
|
||||||
"DownstreamPort": 80,
|
{
|
||||||
|
"Host": "jsonplaceholder.typicode.com",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
"UpstreamPathTemplate": "/posts",
|
"UpstreamPathTemplate": "/posts",
|
||||||
"UpstreamHttpMethod": [ "Post" ],
|
"UpstreamHttpMethod": [ "Post" ],
|
||||||
"QoSOptions": {
|
"QoSOptions": {
|
||||||
@ -111,8 +135,12 @@
|
|||||||
{
|
{
|
||||||
"DownstreamPathTemplate": "/posts/{postId}",
|
"DownstreamPathTemplate": "/posts/{postId}",
|
||||||
"DownstreamScheme": "http",
|
"DownstreamScheme": "http",
|
||||||
"DownstreamHost": "jsonplaceholder.typicode.com",
|
"DownstreamHostAndPorts": [
|
||||||
"DownstreamPort": 80,
|
{
|
||||||
|
"Host": "jsonplaceholder.typicode.com",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
"UpstreamPathTemplate": "/posts/{postId}",
|
"UpstreamPathTemplate": "/posts/{postId}",
|
||||||
"UpstreamHttpMethod": [ "Put" ],
|
"UpstreamHttpMethod": [ "Put" ],
|
||||||
"QoSOptions": {
|
"QoSOptions": {
|
||||||
@ -124,8 +152,12 @@
|
|||||||
{
|
{
|
||||||
"DownstreamPathTemplate": "/posts/{postId}",
|
"DownstreamPathTemplate": "/posts/{postId}",
|
||||||
"DownstreamScheme": "http",
|
"DownstreamScheme": "http",
|
||||||
"DownstreamHost": "jsonplaceholder.typicode.com",
|
"DownstreamHostAndPorts": [
|
||||||
"DownstreamPort": 80,
|
{
|
||||||
|
"Host": "jsonplaceholder.typicode.com",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
"UpstreamPathTemplate": "/posts/{postId}",
|
"UpstreamPathTemplate": "/posts/{postId}",
|
||||||
"UpstreamHttpMethod": [ "Patch" ],
|
"UpstreamHttpMethod": [ "Patch" ],
|
||||||
"QoSOptions": {
|
"QoSOptions": {
|
||||||
@ -137,8 +169,12 @@
|
|||||||
{
|
{
|
||||||
"DownstreamPathTemplate": "/posts/{postId}",
|
"DownstreamPathTemplate": "/posts/{postId}",
|
||||||
"DownstreamScheme": "http",
|
"DownstreamScheme": "http",
|
||||||
"DownstreamHost": "jsonplaceholder.typicode.com",
|
"DownstreamHostAndPorts": [
|
||||||
"DownstreamPort": 80,
|
{
|
||||||
|
"Host": "jsonplaceholder.typicode.com",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
"UpstreamPathTemplate": "/posts/{postId}",
|
"UpstreamPathTemplate": "/posts/{postId}",
|
||||||
"UpstreamHttpMethod": [ "Delete" ],
|
"UpstreamHttpMethod": [ "Delete" ],
|
||||||
"QoSOptions": {
|
"QoSOptions": {
|
||||||
@ -150,8 +186,12 @@
|
|||||||
{
|
{
|
||||||
"DownstreamPathTemplate": "/api/products",
|
"DownstreamPathTemplate": "/api/products",
|
||||||
"DownstreamScheme": "http",
|
"DownstreamScheme": "http",
|
||||||
"DownstreamHost": "jsonplaceholder.typicode.com",
|
"DownstreamHostAndPorts": [
|
||||||
"DownstreamPort": 80,
|
{
|
||||||
|
"Host": "jsonplaceholder.typicode.com",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
"UpstreamPathTemplate": "/products",
|
"UpstreamPathTemplate": "/products",
|
||||||
"UpstreamHttpMethod": [ "Get" ],
|
"UpstreamHttpMethod": [ "Get" ],
|
||||||
"QoSOptions": {
|
"QoSOptions": {
|
||||||
@ -164,8 +204,12 @@
|
|||||||
{
|
{
|
||||||
"DownstreamPathTemplate": "/api/products/{productId}",
|
"DownstreamPathTemplate": "/api/products/{productId}",
|
||||||
"DownstreamScheme": "http",
|
"DownstreamScheme": "http",
|
||||||
"DownstreamHost": "jsonplaceholder.typicode.com",
|
"DownstreamHostAndPorts": [
|
||||||
"DownstreamPort": 80,
|
{
|
||||||
|
"Host": "jsonplaceholder.typicode.com",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
"UpstreamPathTemplate": "/products/{productId}",
|
"UpstreamPathTemplate": "/products/{productId}",
|
||||||
"UpstreamHttpMethod": [ "Get" ],
|
"UpstreamHttpMethod": [ "Get" ],
|
||||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||||
@ -173,8 +217,12 @@
|
|||||||
{
|
{
|
||||||
"DownstreamPathTemplate": "/api/products",
|
"DownstreamPathTemplate": "/api/products",
|
||||||
"DownstreamScheme": "http",
|
"DownstreamScheme": "http",
|
||||||
"DownstreamHost": "products20161126090340.azurewebsites.net",
|
"DownstreamHostAndPorts": [
|
||||||
"DownstreamPort": 80,
|
{
|
||||||
|
"Host": "jsonplaceholder.typicode.com",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
"UpstreamPathTemplate": "/products",
|
"UpstreamPathTemplate": "/products",
|
||||||
"UpstreamHttpMethod": [ "Post" ],
|
"UpstreamHttpMethod": [ "Post" ],
|
||||||
"QoSOptions": {
|
"QoSOptions": {
|
||||||
@ -186,8 +234,12 @@
|
|||||||
{
|
{
|
||||||
"DownstreamPathTemplate": "/api/products/{productId}",
|
"DownstreamPathTemplate": "/api/products/{productId}",
|
||||||
"DownstreamScheme": "http",
|
"DownstreamScheme": "http",
|
||||||
"DownstreamHost": "products20161126090340.azurewebsites.net",
|
"DownstreamHostAndPorts": [
|
||||||
"DownstreamPort": 80,
|
{
|
||||||
|
"Host": "jsonplaceholder.typicode.com",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
"UpstreamPathTemplate": "/products/{productId}",
|
"UpstreamPathTemplate": "/products/{productId}",
|
||||||
"UpstreamHttpMethod": [ "Put" ],
|
"UpstreamHttpMethod": [ "Put" ],
|
||||||
"QoSOptions": {
|
"QoSOptions": {
|
||||||
@ -197,95 +249,15 @@
|
|||||||
},
|
},
|
||||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/api/products/{productId}",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHost": "products20161126090340.azurewebsites.net",
|
|
||||||
"DownstreamPort": 80,
|
|
||||||
"UpstreamPathTemplate": "/products/{productId}",
|
|
||||||
"UpstreamHttpMethod": [ "Delete" ],
|
|
||||||
"QoSOptions": {
|
|
||||||
"ExceptionsAllowedBeforeBreaking": 3,
|
|
||||||
"DurationOfBreak": 10,
|
|
||||||
"TimeoutValue": 5000
|
|
||||||
},
|
|
||||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/api/customers",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHost": "customers20161126090811.azurewebsites.net",
|
|
||||||
"DownstreamPort": 80,
|
|
||||||
"UpstreamPathTemplate": "/customers",
|
|
||||||
"UpstreamHttpMethod": [ "Get" ],
|
|
||||||
"QoSOptions": {
|
|
||||||
"ExceptionsAllowedBeforeBreaking": 3,
|
|
||||||
"DurationOfBreak": 10,
|
|
||||||
"TimeoutValue": 5000
|
|
||||||
},
|
|
||||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/api/customers/{customerId}",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHost": "customers20161126090811.azurewebsites.net",
|
|
||||||
"DownstreamPort": 80,
|
|
||||||
"UpstreamPathTemplate": "/customers/{customerId}",
|
|
||||||
"UpstreamHttpMethod": [ "Get" ],
|
|
||||||
"QoSOptions": {
|
|
||||||
"ExceptionsAllowedBeforeBreaking": 3,
|
|
||||||
"DurationOfBreak": 10,
|
|
||||||
"TimeoutValue": 5000
|
|
||||||
},
|
|
||||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/api/customers",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHost": "customers20161126090811.azurewebsites.net",
|
|
||||||
"DownstreamPort": 80,
|
|
||||||
"UpstreamPathTemplate": "/customers",
|
|
||||||
"UpstreamHttpMethod": [ "Post" ],
|
|
||||||
"QoSOptions": {
|
|
||||||
"ExceptionsAllowedBeforeBreaking": 3,
|
|
||||||
"DurationOfBreak": 10,
|
|
||||||
"TimeoutValue": 5000
|
|
||||||
},
|
|
||||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/api/customers/{customerId}",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHost": "customers20161126090811.azurewebsites.net",
|
|
||||||
"DownstreamPort": 80,
|
|
||||||
"UpstreamPathTemplate": "/customers/{customerId}",
|
|
||||||
"UpstreamHttpMethod": [ "Put" ],
|
|
||||||
"QoSOptions": {
|
|
||||||
"ExceptionsAllowedBeforeBreaking": 3,
|
|
||||||
"DurationOfBreak": 10,
|
|
||||||
"TimeoutValue": 5000
|
|
||||||
},
|
|
||||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/api/customers/{customerId}",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHost": "customers20161126090811.azurewebsites.net",
|
|
||||||
"DownstreamPort": 80,
|
|
||||||
"UpstreamPathTemplate": "/customers/{customerId}",
|
|
||||||
"UpstreamHttpMethod": [ "Delete" ],
|
|
||||||
"QoSOptions": {
|
|
||||||
"ExceptionsAllowedBeforeBreaking": 3,
|
|
||||||
"DurationOfBreak": 10,
|
|
||||||
"TimeoutValue": 5000
|
|
||||||
},
|
|
||||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"DownstreamPathTemplate": "/posts",
|
"DownstreamPathTemplate": "/posts",
|
||||||
"DownstreamScheme": "http",
|
"DownstreamScheme": "http",
|
||||||
"DownstreamHost": "jsonplaceholder.typicode.com",
|
"DownstreamHostAndPorts": [
|
||||||
"DownstreamPort": 80,
|
{
|
||||||
|
"Host": "jsonplaceholder.typicode.com",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
"UpstreamPathTemplate": "/posts/",
|
"UpstreamPathTemplate": "/posts/",
|
||||||
"UpstreamHttpMethod": [ "Get" ],
|
"UpstreamHttpMethod": [ "Get" ],
|
||||||
"QoSOptions": {
|
"QoSOptions": {
|
||||||
@ -298,8 +270,12 @@
|
|||||||
{
|
{
|
||||||
"DownstreamPathTemplate": "/",
|
"DownstreamPathTemplate": "/",
|
||||||
"DownstreamScheme": "http",
|
"DownstreamScheme": "http",
|
||||||
"DownstreamHost": "www.bbc.co.uk",
|
"DownstreamHostAndPorts": [
|
||||||
"DownstreamPort": 80,
|
{
|
||||||
|
"Host": "www.bbc.co.uk",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
"UpstreamPathTemplate": "/bbc/",
|
"UpstreamPathTemplate": "/bbc/",
|
||||||
"UpstreamHttpMethod": [ "Get" ]
|
"UpstreamHttpMethod": [ "Get" ]
|
||||||
}
|
}
|
||||||
|
@ -269,6 +269,46 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void configuration_is_valid_with_duplicate_reroutes_all_verbs_but_different_hosts()
|
||||||
|
{
|
||||||
|
this.Given(x => x.GivenAConfiguration(new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/api/products/",
|
||||||
|
UpstreamPathTemplate = "/asdf/",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "bb.co.uk"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpstreamHost = "host1"
|
||||||
|
},
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/www/test/",
|
||||||
|
UpstreamPathTemplate = "/asdf/",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "bb.co.uk"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpstreamHost = "host1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.When(x => x.WhenIValidateTheConfiguration())
|
||||||
|
.Then(x => x.ThenTheResultIsValid())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void configuration_is_not_valid_with_duplicate_reroutes_specific_verbs()
|
public void configuration_is_not_valid_with_duplicate_reroutes_specific_verbs()
|
||||||
{
|
{
|
||||||
|
@ -75,7 +75,7 @@
|
|||||||
{
|
{
|
||||||
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
|
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
|
||||||
_downstreamRouteFinder
|
_downstreamRouteFinder
|
||||||
.Setup(x => x.FindDownstreamRoute(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IOcelotConfiguration>()))
|
.Setup(x => x.FindDownstreamRoute(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IOcelotConfiguration>(), It.IsAny<string>()))
|
||||||
.Returns(_downstreamRoute);
|
.Returns(_downstreamRoute);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
private OcelotConfiguration _config;
|
private OcelotConfiguration _config;
|
||||||
private Response<UrlMatch> _match;
|
private Response<UrlMatch> _match;
|
||||||
private string _upstreamHttpMethod;
|
private string _upstreamHttpMethod;
|
||||||
|
private string _upstreamHost;
|
||||||
|
|
||||||
public DownstreamRouteFinderTests()
|
public DownstreamRouteFinderTests()
|
||||||
{
|
{
|
||||||
@ -210,7 +211,6 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||||
.Build()
|
.Build()
|
||||||
)))
|
)))
|
||||||
.And(x => x.ThenTheUrlMatcherIsNotCalled())
|
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -370,12 +370,161 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
|
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
|
||||||
.And(x => x.GivenTheUpstreamHttpMethodIs("Post"))
|
.And(x => x.GivenTheUpstreamHttpMethodIs("Post"))
|
||||||
.When(x => x.WhenICallTheFinder())
|
.When(x => x.WhenICallTheFinder())
|
||||||
.Then(
|
.Then(x => x.ThenAnErrorResponseIsReturned())
|
||||||
x => x.ThenAnErrorResponseIsReturned())
|
|
||||||
.And(x => x.ThenTheUrlMatcherIsNotCalled())
|
.And(x => x.ThenTheUrlMatcherIsNotCalled())
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_route_when_host_matches()
|
||||||
|
{
|
||||||
|
var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build();
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/"))
|
||||||
|
.And(x => GivenTheUpstreamHostIs("MATCH"))
|
||||||
|
.And(x => x.GivenTheTemplateVariableAndNameFinderReturns(
|
||||||
|
new OkResponse<List<PlaceholderNameAndValue>>(
|
||||||
|
new List<PlaceholderNameAndValue>())))
|
||||||
|
.And(x => x.GivenTheConfigurationIs(new List<ReRoute>
|
||||||
|
{
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||||
|
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||||
|
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||||
|
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||||
|
.WithUpstreamHost("MATCH")
|
||||||
|
.Build()
|
||||||
|
}, string.Empty, serviceProviderConfig
|
||||||
|
))
|
||||||
|
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
|
||||||
|
.And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
|
||||||
|
.When(x => x.WhenICallTheFinder())
|
||||||
|
.Then(
|
||||||
|
x => x.ThenTheFollowingIsReturned(new DownstreamRoute(
|
||||||
|
new List<PlaceholderNameAndValue>(),
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||||
|
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||||
|
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||||
|
.Build()
|
||||||
|
)))
|
||||||
|
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_route_when_upstreamhost_is_null()
|
||||||
|
{
|
||||||
|
var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build();
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/"))
|
||||||
|
.And(x => GivenTheUpstreamHostIs("MATCH"))
|
||||||
|
.And(x => x.GivenTheTemplateVariableAndNameFinderReturns(
|
||||||
|
new OkResponse<List<PlaceholderNameAndValue>>(
|
||||||
|
new List<PlaceholderNameAndValue>())))
|
||||||
|
.And(x => x.GivenTheConfigurationIs(new List<ReRoute>
|
||||||
|
{
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||||
|
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||||
|
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||||
|
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||||
|
.Build()
|
||||||
|
}, string.Empty, serviceProviderConfig
|
||||||
|
))
|
||||||
|
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
|
||||||
|
.And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
|
||||||
|
.When(x => x.WhenICallTheFinder())
|
||||||
|
.Then(
|
||||||
|
x => x.ThenTheFollowingIsReturned(new DownstreamRoute(
|
||||||
|
new List<PlaceholderNameAndValue>(),
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||||
|
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||||
|
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||||
|
.Build()
|
||||||
|
)))
|
||||||
|
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_not_return_route_when_host_doesnt_match()
|
||||||
|
{
|
||||||
|
var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build();
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/"))
|
||||||
|
.And(x => GivenTheUpstreamHostIs("DONTMATCH"))
|
||||||
|
.And(x => x.GivenTheTemplateVariableAndNameFinderReturns(new OkResponse<List<PlaceholderNameAndValue>>(new List<PlaceholderNameAndValue>())))
|
||||||
|
.And(x => x.GivenTheConfigurationIs(new List<ReRoute>
|
||||||
|
{
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||||
|
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||||
|
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||||
|
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||||
|
.WithUpstreamHost("MATCH")
|
||||||
|
|
||||||
|
.Build()
|
||||||
|
}, string.Empty, serviceProviderConfig
|
||||||
|
))
|
||||||
|
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
|
||||||
|
.And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
|
||||||
|
.When(x => x.WhenICallTheFinder())
|
||||||
|
.Then(x => x.ThenAnErrorResponseIsReturned())
|
||||||
|
.And(x => x.ThenTheUrlMatcherIsNotCalled())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_route_when_host_matches_but_null_host_on_same_path_first()
|
||||||
|
{
|
||||||
|
var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build();
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/"))
|
||||||
|
.And(x => GivenTheUpstreamHostIs("MATCH"))
|
||||||
|
.And(x => x.GivenTheTemplateVariableAndNameFinderReturns(
|
||||||
|
new OkResponse<List<PlaceholderNameAndValue>>(
|
||||||
|
new List<PlaceholderNameAndValue>())))
|
||||||
|
.And(x => x.GivenTheConfigurationIs(new List<ReRoute>
|
||||||
|
{
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamPathTemplate("THENULLPATH")
|
||||||
|
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||||
|
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||||
|
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||||
|
.Build(),
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||||
|
.WithUpstreamPathTemplate("someUpstreamPath")
|
||||||
|
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||||
|
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||||
|
.WithUpstreamHost("MATCH")
|
||||||
|
.Build()
|
||||||
|
}, string.Empty, serviceProviderConfig
|
||||||
|
))
|
||||||
|
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
|
||||||
|
.And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
|
||||||
|
.When(x => x.WhenICallTheFinder())
|
||||||
|
.Then(
|
||||||
|
x => x.ThenTheFollowingIsReturned(new DownstreamRoute(
|
||||||
|
new List<PlaceholderNameAndValue>(),
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||||
|
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||||
|
.WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1))
|
||||||
|
.Build()
|
||||||
|
)))
|
||||||
|
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly(2))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheUpstreamHostIs(string upstreamHost)
|
||||||
|
{
|
||||||
|
_upstreamHost = upstreamHost;
|
||||||
|
}
|
||||||
|
|
||||||
private void GivenTheTemplateVariableAndNameFinderReturns(Response<List<PlaceholderNameAndValue>> response)
|
private void GivenTheTemplateVariableAndNameFinderReturns(Response<List<PlaceholderNameAndValue>> response)
|
||||||
{
|
{
|
||||||
_finder
|
_finder
|
||||||
@ -399,6 +548,12 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
.Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Once);
|
.Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Once);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ThenTheUrlMatcherIsCalledCorrectly(int times)
|
||||||
|
{
|
||||||
|
_mockMatcher
|
||||||
|
.Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Exactly(times));
|
||||||
|
}
|
||||||
|
|
||||||
private void ThenTheUrlMatcherIsCalledCorrectly(string expectedUpstreamUrlPath)
|
private void ThenTheUrlMatcherIsCalledCorrectly(string expectedUpstreamUrlPath)
|
||||||
{
|
{
|
||||||
_mockMatcher
|
_mockMatcher
|
||||||
@ -432,7 +587,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
|
|
||||||
private void WhenICallTheFinder()
|
private void WhenICallTheFinder()
|
||||||
{
|
{
|
||||||
_result = _downstreamRouteFinder.FindDownstreamRoute(_upstreamUrlPath, _upstreamHttpMethod, _config);
|
_result = _downstreamRouteFinder.FindDownstreamRoute(_upstreamUrlPath, _upstreamHttpMethod, _config, _upstreamHost);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThenTheFollowingIsReturned(DownstreamRoute expected)
|
private void ThenTheFollowingIsReturned(DownstreamRoute expected)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user