+semver: breaking PRs merged from Develop, Eureka honour scheme, don't crash on dispose and validate duplicate placeholders in UpstreamPathTemplate

* initial commit for new feature #1077

allow to limit the number of concurrent tcp connection to a downstream service

* protect code against value not in accurate range

add unit test

* Do not crash host on Dispose

* Add test

* Pin GitVersion.CommandLine package version

* #683 validate if there are duplicated placeholders in UpstreamPathTemplate

* Use registered scheme from Eureka (#1087)

* extra test

* very brief mention MaxConnectionsPerServer in docs

* build develop like a PR

* more docs

Co-authored-by: jlukawska <56401969+jlukawska@users.noreply.github.com>
Co-authored-by: buretjph <58700930+buretjph@users.noreply.github.com>
Co-authored-by: Jonathan Mezach <jonathanmezach@gmail.com>
Co-authored-by: 彭伟 <pengweiqhca@sina.com>
This commit is contained in:
Tom Pallister 2020-01-19 15:00:21 +00:00 committed by GitHub
parent 664c6ef626
commit 65710f4a94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 792 additions and 560 deletions

View File

@ -27,4 +27,3 @@ workflows:
branches: branches:
ignore: ignore:
- master - master
- develop

View File

@ -1,4 +1,4 @@
#tool "nuget:?package=GitVersion.CommandLine&version=5.0.1" #tool "nuget:?package=GitVersion.CommandLine&version=5.0.1"
#tool "nuget:?package=GitReleaseNotes" #tool "nuget:?package=GitReleaseNotes"
#addin nuget:?package=Cake.Json #addin nuget:?package=Cake.Json
#addin nuget:?package=Newtonsoft.Json #addin nuget:?package=Newtonsoft.Json

View File

@ -62,7 +62,8 @@ Here is an example ReRoute configuration, You don't need to set all of these thi
"HttpHandlerOptions": { "HttpHandlerOptions": {
"AllowAutoRedirect": true, "AllowAutoRedirect": true,
"UseCookieContainer": true, "UseCookieContainer": true,
"UseTracing": true "UseTracing": true,
"MaxConnectionsPerServer": 100
}, },
"DangerousAcceptAnyServerCertificateValidator": false "DangerousAcceptAnyServerCertificateValidator": false
} }
@ -222,3 +223,8 @@ If you want to ignore SSL warnings / errors set the following in your ReRoute co
"DangerousAcceptAnyServerCertificateValidator": true "DangerousAcceptAnyServerCertificateValidator": true
I don't recommend doing this, I suggest creating your own certificate and then getting it trusted by your local / remote machine if you can. I don't recommend doing this, I suggest creating your own certificate and then getting it trusted by your local / remote machine if you can.
MaxConnectionsPerServer
^^^^^^^^^^^^^^^^^^^^^^^
This controls how many connections the internal HttpClient will open. This can be set at ReRoute or global level.

View File

@ -155,6 +155,8 @@ Eureka. One of the services polls Eureka every 30 seconds (default) and gets the
When Ocelot asks for a given service it is retrieved from memory so performance is not a big problem. Please note that this code When Ocelot asks for a given service it is retrieved from memory so performance is not a big problem. Please note that this code
is provided by the Pivotal.Discovery.Client NuGet package so big thanks to them for all the hard work. is provided by the Pivotal.Discovery.Client NuGet package so big thanks to them for all the hard work.
Ocelot will use the scheme (http/https) set in Eureka if these values are not provided in ocelot.json
Dynamic Routing Dynamic Routing
^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^

View File

@ -26,7 +26,7 @@
if (instances != null && instances.Any()) if (instances != null && instances.Any())
{ {
services.AddRange(instances.Select(i => new Service(i.ServiceId, new ServiceHostAndPort(i.Host, i.Port), "", "", new List<string>()))); services.AddRange(instances.Select(i => new Service(i.ServiceId, new ServiceHostAndPort(i.Host, i.Port, i.Uri.Scheme), "", "", new List<string>())));
} }
return Task.FromResult(services); return Task.FromResult(services);

View File

@ -18,8 +18,11 @@
{ {
var useTracing = _tracer != null && options.UseTracing; var useTracing = _tracer != null && options.UseTracing;
//be sure that maxConnectionPerServer is in correct range of values
int maxConnectionPerServer = (options.MaxConnectionsPerServer > 0) ? maxConnectionPerServer = options.MaxConnectionsPerServer : maxConnectionPerServer = int.MaxValue;
return new HttpHandlerOptions(options.AllowAutoRedirect, return new HttpHandlerOptions(options.AllowAutoRedirect,
options.UseCookieContainer, useTracing, options.UseProxy); options.UseCookieContainer, useTracing, options.UseProxy, maxConnectionPerServer);
} }
} }
} }

View File

@ -7,6 +7,7 @@
AllowAutoRedirect = false; AllowAutoRedirect = false;
UseCookieContainer = false; UseCookieContainer = false;
UseProxy = true; UseProxy = true;
MaxConnectionsPerServer = int.MaxValue;
} }
public bool AllowAutoRedirect { get; set; } public bool AllowAutoRedirect { get; set; }
@ -16,5 +17,7 @@
public bool UseTracing { get; set; } public bool UseTracing { get; set; }
public bool UseProxy { get; set; } public bool UseProxy { get; set; }
public int MaxConnectionsPerServer { get; set; }
} }
} }

View File

@ -6,32 +6,44 @@
/// </summary> /// </summary>
public class HttpHandlerOptions public class HttpHandlerOptions
{ {
public HttpHandlerOptions(bool allowAutoRedirect, bool useCookieContainer, bool useTracing, bool useProxy) public HttpHandlerOptions(bool allowAutoRedirect, bool useCookieContainer, bool useTracing, bool useProxy, int maxConnectionsPerServer)
{ {
AllowAutoRedirect = allowAutoRedirect; AllowAutoRedirect = allowAutoRedirect;
UseCookieContainer = useCookieContainer; UseCookieContainer = useCookieContainer;
UseTracing = useTracing; UseTracing = useTracing;
UseProxy = useProxy; UseProxy = useProxy;
MaxConnectionsPerServer = maxConnectionsPerServer;
} }
/// <summary> /// <summary>
/// Specify if auto redirect is enabled /// Specify if auto redirect is enabled
/// </summary> /// </summary>
/// <value>AllowAutoRedirect</value>
public bool AllowAutoRedirect { get; private set; } public bool AllowAutoRedirect { get; private set; }
/// <summary> /// <summary>
/// Specify is handler has to use a cookie container /// Specify is handler has to use a cookie container
/// </summary> /// </summary>
/// <value>UseCookieContainer</value>
public bool UseCookieContainer { get; private set; } public bool UseCookieContainer { get; private set; }
/// <summary> /// <summary>
/// Specify is handler has to use a opentracing /// Specify is handler has to use a opentracing
/// </summary> /// </summary>
/// <value>UseTracing</value>
public bool UseTracing { get; private set; } public bool UseTracing { get; private set; }
/// <summary> /// <summary>
/// Specify if handler has to use a proxy /// Specify if handler has to use a proxy
/// </summary> /// </summary>
/// <value>UseProxy</value>
public bool UseProxy { get; private set; } public bool UseProxy { get; private set; }
/// <summary>
/// Specify the maximum of concurrent connection to a network endpoint
/// </summary>
/// <value>MaxConnectionsPerServer</value>
public int MaxConnectionsPerServer { get; private set; }
} }
} }

View File

@ -6,6 +6,7 @@
private bool _useCookieContainer; private bool _useCookieContainer;
private bool _useTracing; private bool _useTracing;
private bool _useProxy; private bool _useProxy;
private int _maxConnectionPerServer;
public HttpHandlerOptionsBuilder WithAllowAutoRedirect(bool input) public HttpHandlerOptionsBuilder WithAllowAutoRedirect(bool input)
{ {
@ -30,10 +31,16 @@
_useProxy = useProxy; _useProxy = useProxy;
return this; return this;
} }
public HttpHandlerOptionsBuilder WithUseMaxConnectionPerServer(int maxConnectionPerServer)
{
_maxConnectionPerServer = maxConnectionPerServer;
return this;
}
public HttpHandlerOptions Build() public HttpHandlerOptions Build()
{ {
return new HttpHandlerOptions(_allowAutoRedirect, _useCookieContainer, _useTracing, _useProxy); return new HttpHandlerOptions(_allowAutoRedirect, _useCookieContainer, _useTracing, _useProxy, _maxConnectionPerServer);
} }
} }
} }

View File

@ -105,7 +105,8 @@ namespace Ocelot.Configuration.Repository
public void Dispose() public void Dispose()
{ {
_timer.Dispose(); _timer?.Dispose();
_timer = null;
} }
} }
} }

View File

@ -9,6 +9,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
public class FileConfigurationFluentValidator : AbstractValidator<FileConfiguration>, IConfigurationValidator public class FileConfigurationFluentValidator : AbstractValidator<FileConfiguration>, IConfigurationValidator
@ -35,6 +36,10 @@
.Must((config, reRoute) => HaveServiceDiscoveryProviderRegistered(reRoute, config.GlobalConfiguration.ServiceDiscoveryProvider)) .Must((config, reRoute) => HaveServiceDiscoveryProviderRegistered(reRoute, config.GlobalConfiguration.ServiceDiscoveryProvider))
.WithMessage((config, reRoute) => $"Unable to start Ocelot, errors are: Unable to start Ocelot because either a ReRoute or GlobalConfiguration are using ServiceDiscoveryOptions but no ServiceDiscoveryFinderDelegate has been registered in dependency injection container. Are you missing a package like Ocelot.Provider.Consul and services.AddConsul() or Ocelot.Provider.Eureka and services.AddEureka()?"); .WithMessage((config, reRoute) => $"Unable to start Ocelot, errors are: Unable to start Ocelot because either a ReRoute or GlobalConfiguration are using ServiceDiscoveryOptions but no ServiceDiscoveryFinderDelegate has been registered in dependency injection container. Are you missing a package like Ocelot.Provider.Consul and services.AddConsul() or Ocelot.Provider.Eureka and services.AddEureka()?");
RuleForEach(configuration => configuration.ReRoutes)
.Must((config, reRoute) => IsPlaceholderNotDuplicatedIn(reRoute.UpstreamPathTemplate))
.WithMessage((config, reRoute) => $"{nameof(reRoute)} {reRoute.UpstreamPathTemplate} has duplicated placeholder");
RuleFor(configuration => configuration.GlobalConfiguration.ServiceDiscoveryProvider) RuleFor(configuration => configuration.GlobalConfiguration.ServiceDiscoveryProvider)
.Must(HaveServiceDiscoveryProviderRegistered) .Must(HaveServiceDiscoveryProviderRegistered)
.WithMessage((config, reRoute) => $"Unable to start Ocelot, errors are: Unable to start Ocelot because either a ReRoute or GlobalConfiguration are using ServiceDiscoveryOptions but no ServiceDiscoveryFinderDelegate has been registered in dependency injection container. Are you missing a package like Ocelot.Provider.Consul and services.AddConsul() or Ocelot.Provider.Eureka and services.AddEureka()?"); .WithMessage((config, reRoute) => $"Unable to start Ocelot, errors are: Unable to start Ocelot because either a ReRoute or GlobalConfiguration are using ServiceDiscoveryOptions but no ServiceDiscoveryFinderDelegate has been registered in dependency injection container. Are you missing a package like Ocelot.Provider.Consul and services.AddConsul() or Ocelot.Provider.Eureka and services.AddEureka()?");
@ -109,6 +114,14 @@
return reRoutesForAggregate.Count() == fileAggregateReRoute.ReRouteKeys.Count; return reRoutesForAggregate.Count() == fileAggregateReRoute.ReRouteKeys.Count;
} }
private bool IsPlaceholderNotDuplicatedIn(string upstreamPathTemplate)
{
Regex regExPlaceholder = new Regex("{[^}]+}");
var matches = regExPlaceholder.Matches(upstreamPathTemplate);
var upstreamPathPlaceholders = matches.Select(m => m.Value);
return upstreamPathPlaceholders.Count() == upstreamPathPlaceholders.Distinct().Count();
}
private static bool DoesNotContainReRoutesWithSpecificRequestIdKeys(FileAggregateReRoute fileAggregateReRoute, private static bool DoesNotContainReRoutesWithSpecificRequestIdKeys(FileAggregateReRoute fileAggregateReRoute,
List<FileReRoute> reRoutes) List<FileReRoute> reRoutes)
{ {

View File

@ -37,7 +37,10 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
return; return;
} }
context.DownstreamRequest.Scheme = context.DownstreamReRoute.DownstreamScheme; if (!string.IsNullOrEmpty(context.DownstreamReRoute.DownstreamScheme))
{
context.DownstreamRequest.Scheme = context.DownstreamReRoute.DownstreamScheme;
}
if (ServiceFabricRequest(context)) if (ServiceFabricRequest(context))
{ {

View File

@ -45,6 +45,11 @@ namespace Ocelot.LoadBalancer.Middleware
context.DownstreamRequest.Port = hostAndPort.Data.DownstreamPort; context.DownstreamRequest.Port = hostAndPort.Data.DownstreamPort;
} }
if (!string.IsNullOrEmpty(hostAndPort.Data.Scheme))
{
context.DownstreamRequest.Scheme = hostAndPort.Data.Scheme;
}
try try
{ {
await _next.Invoke(context); await _next.Invoke(context);

View File

@ -90,7 +90,9 @@ namespace Ocelot.Requester
{ {
AllowAutoRedirect = context.DownstreamReRoute.HttpHandlerOptions.AllowAutoRedirect, AllowAutoRedirect = context.DownstreamReRoute.HttpHandlerOptions.AllowAutoRedirect,
UseCookies = context.DownstreamReRoute.HttpHandlerOptions.UseCookieContainer, UseCookies = context.DownstreamReRoute.HttpHandlerOptions.UseCookieContainer,
UseProxy = context.DownstreamReRoute.HttpHandlerOptions.UseProxy UseProxy = context.DownstreamReRoute.HttpHandlerOptions.UseProxy,
MaxConnectionsPerServer = context.DownstreamReRoute.HttpHandlerOptions.MaxConnectionsPerServer
}; };
} }
@ -101,6 +103,7 @@ namespace Ocelot.Requester
AllowAutoRedirect = context.DownstreamReRoute.HttpHandlerOptions.AllowAutoRedirect, AllowAutoRedirect = context.DownstreamReRoute.HttpHandlerOptions.AllowAutoRedirect,
UseCookies = context.DownstreamReRoute.HttpHandlerOptions.UseCookieContainer, UseCookies = context.DownstreamReRoute.HttpHandlerOptions.UseCookieContainer,
UseProxy = context.DownstreamReRoute.HttpHandlerOptions.UseProxy, UseProxy = context.DownstreamReRoute.HttpHandlerOptions.UseProxy,
MaxConnectionsPerServer = context.DownstreamReRoute.HttpHandlerOptions.MaxConnectionsPerServer,
CookieContainer = new CookieContainer() CookieContainer = new CookieContainer()
}; };
} }

View File

@ -34,7 +34,7 @@ namespace Ocelot.ServiceDiscovery
foreach (var downstreamAddress in reRoute.DownstreamAddresses) foreach (var downstreamAddress in reRoute.DownstreamAddresses)
{ {
var service = new Service(reRoute.ServiceName, new ServiceHostAndPort(downstreamAddress.Host, downstreamAddress.Port), string.Empty, string.Empty, new string[0]); var service = new Service(reRoute.ServiceName, new ServiceHostAndPort(downstreamAddress.Host, downstreamAddress.Port, reRoute.DownstreamScheme), string.Empty, string.Empty, new string[0]);
services.Add(service); services.Add(service);
} }

View File

@ -8,8 +8,13 @@
DownstreamPort = downstreamPort; DownstreamPort = downstreamPort;
} }
public ServiceHostAndPort(string downstreamHost, int downstreamPort, string scheme)
: this(downstreamHost, downstreamPort) => Scheme = scheme;
public string DownstreamHost { get; } public string DownstreamHost { get; }
public int DownstreamPort { get; } public int DownstreamPort { get; }
public string Scheme { get; }
} }
} }

View File

@ -41,14 +41,14 @@ namespace Ocelot.UnitTests.Configuration
_internalConfigCreator = new Mock<IInternalConfigurationCreator>(); _internalConfigCreator = new Mock<IInternalConfigurationCreator>();
_internalConfigCreator.Setup(x => x.Create(It.IsAny<FileConfiguration>())).ReturnsAsync(new OkResponse<IInternalConfiguration>(_internalConfig)); _internalConfigCreator.Setup(x => x.Create(It.IsAny<FileConfiguration>())).ReturnsAsync(new OkResponse<IInternalConfiguration>(_internalConfig));
_poller = new FileConfigurationPoller(_factory.Object, _repo.Object, _config.Object, _internalConfigRepo.Object, _internalConfigCreator.Object); _poller = new FileConfigurationPoller(_factory.Object, _repo.Object, _config.Object, _internalConfigRepo.Object, _internalConfigCreator.Object);
_poller.StartAsync(new CancellationToken());
} }
[Fact] [Fact]
public void should_start() public void should_start()
{ {
this.Given(x => ThenTheSetterIsCalled(_fileConfig, 1)) this.Given(x => GivenPollerHasStarted())
.BDDfy(); .Given(x => ThenTheSetterIsCalled(_fileConfig, 1))
.BDDfy();
} }
[Fact] [Fact]
@ -71,7 +71,8 @@ namespace Ocelot.UnitTests.Configuration
} }
}; };
this.Given(x => WhenTheConfigIsChanged(newConfig, 0)) this.Given(x => GivenPollerHasStarted())
.Given(x => WhenTheConfigIsChanged(newConfig, 0))
.Then(x => ThenTheSetterIsCalledAtLeast(newConfig, 1)) .Then(x => ThenTheSetterIsCalledAtLeast(newConfig, 1))
.BDDfy(); .BDDfy();
} }
@ -96,7 +97,8 @@ namespace Ocelot.UnitTests.Configuration
} }
}; };
this.Given(x => WhenTheConfigIsChanged(newConfig, 10)) this.Given(x => GivenPollerHasStarted())
.Given(x => WhenTheConfigIsChanged(newConfig, 10))
.Then(x => ThenTheSetterIsCalled(newConfig, 1)) .Then(x => ThenTheSetterIsCalled(newConfig, 1))
.BDDfy(); .BDDfy();
} }
@ -121,11 +123,24 @@ namespace Ocelot.UnitTests.Configuration
} }
}; };
this.Given(x => WhenProviderErrors()) this.Given(x => GivenPollerHasStarted())
.Given(x => WhenProviderErrors())
.Then(x => ThenTheSetterIsCalled(newConfig, 0)) .Then(x => ThenTheSetterIsCalled(newConfig, 0))
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_dispose_cleanly_without_starting()
{
this.When(x => WhenPollerIsDisposed())
.BDDfy();
}
private void GivenPollerHasStarted()
{
_poller.StartAsync(CancellationToken.None);
}
private void WhenProviderErrors() private void WhenProviderErrors()
{ {
_repo _repo
@ -141,6 +156,11 @@ namespace Ocelot.UnitTests.Configuration
.ReturnsAsync(new OkResponse<FileConfiguration>(newConfig)); .ReturnsAsync(new OkResponse<FileConfiguration>(newConfig));
} }
private void WhenPollerIsDisposed()
{
_poller.Dispose();
}
private void ThenTheSetterIsCalled(FileConfiguration fileConfig, int times) private void ThenTheSetterIsCalled(FileConfiguration fileConfig, int times)
{ {
var result = WaitFor(4000).Until(() => var result = WaitFor(4000).Until(() =>

View File

@ -41,7 +41,7 @@ namespace Ocelot.UnitTests.Configuration
} }
}; };
var expectedOptions = new HttpHandlerOptions(false, false, false, true); var expectedOptions = new HttpHandlerOptions(false, false, false, true, int.MaxValue);
this.Given(x => GivenTheFollowing(fileReRoute)) this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions()) .When(x => WhenICreateHttpHandlerOptions())
@ -60,7 +60,7 @@ namespace Ocelot.UnitTests.Configuration
} }
}; };
var expectedOptions = new HttpHandlerOptions(false, false, true, true); var expectedOptions = new HttpHandlerOptions(false, false, true, true, int.MaxValue);
this.Given(x => GivenTheFollowing(fileReRoute)) this.Given(x => GivenTheFollowing(fileReRoute))
.And(x => GivenARealTracer()) .And(x => GivenARealTracer())
@ -73,7 +73,7 @@ namespace Ocelot.UnitTests.Configuration
public void should_create_options_with_useCookie_false_and_allowAutoRedirect_true_as_default() public void should_create_options_with_useCookie_false_and_allowAutoRedirect_true_as_default()
{ {
var fileReRoute = new FileReRoute(); var fileReRoute = new FileReRoute();
var expectedOptions = new HttpHandlerOptions(false, false, false, true); var expectedOptions = new HttpHandlerOptions(false, false, false, true, int.MaxValue);
this.Given(x => GivenTheFollowing(fileReRoute)) this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions()) .When(x => WhenICreateHttpHandlerOptions())
@ -94,7 +94,7 @@ namespace Ocelot.UnitTests.Configuration
} }
}; };
var expectedOptions = new HttpHandlerOptions(false, false, false, true); var expectedOptions = new HttpHandlerOptions(false, false, false, true, int.MaxValue);
this.Given(x => GivenTheFollowing(fileReRoute)) this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions()) .When(x => WhenICreateHttpHandlerOptions())
@ -110,7 +110,7 @@ namespace Ocelot.UnitTests.Configuration
HttpHandlerOptions = new FileHttpHandlerOptions() HttpHandlerOptions = new FileHttpHandlerOptions()
}; };
var expectedOptions = new HttpHandlerOptions(false, false, false, true); var expectedOptions = new HttpHandlerOptions(false, false, false, true, int.MaxValue);
this.Given(x => GivenTheFollowing(fileReRoute)) this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions()) .When(x => WhenICreateHttpHandlerOptions())
@ -129,7 +129,64 @@ namespace Ocelot.UnitTests.Configuration
} }
}; };
var expectedOptions = new HttpHandlerOptions(false, false, false, false); var expectedOptions = new HttpHandlerOptions(false, false, false, false, int.MaxValue);
this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions())
.Then(x => ThenTheFollowingOptionsReturned(expectedOptions))
.BDDfy();
}
[Fact]
public void should_create_options_with_specified_MaxConnectionsPerServer()
{
var fileReRoute = new FileReRoute
{
HttpHandlerOptions = new FileHttpHandlerOptions
{
MaxConnectionsPerServer = 10
}
};
var expectedOptions = new HttpHandlerOptions(false, false, false, true, 10);
this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions())
.Then(x => ThenTheFollowingOptionsReturned(expectedOptions))
.BDDfy();
}
[Fact]
public void should_create_options_fixing_specified_MaxConnectionsPerServer_range()
{
var fileReRoute = new FileReRoute
{
HttpHandlerOptions = new FileHttpHandlerOptions
{
MaxConnectionsPerServer = -1
}
};
var expectedOptions = new HttpHandlerOptions(false, false, false, true, int.MaxValue);
this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions())
.Then(x => ThenTheFollowingOptionsReturned(expectedOptions))
.BDDfy();
}
[Fact]
public void should_create_options_fixing_specified_MaxConnectionsPerServer_range_when_zero()
{
var fileReRoute = new FileReRoute
{
HttpHandlerOptions = new FileHttpHandlerOptions
{
MaxConnectionsPerServer = 0
}
};
var expectedOptions = new HttpHandlerOptions(false, false, false, true, int.MaxValue);
this.Given(x => GivenTheFollowing(fileReRoute)) this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions()) .When(x => WhenICreateHttpHandlerOptions())
@ -154,6 +211,7 @@ namespace Ocelot.UnitTests.Configuration
_httpHandlerOptions.UseCookieContainer.ShouldBe(expected.UseCookieContainer); _httpHandlerOptions.UseCookieContainer.ShouldBe(expected.UseCookieContainer);
_httpHandlerOptions.UseTracing.ShouldBe(expected.UseTracing); _httpHandlerOptions.UseTracing.ShouldBe(expected.UseTracing);
_httpHandlerOptions.UseProxy.ShouldBe(expected.UseProxy); _httpHandlerOptions.UseProxy.ShouldBe(expected.UseProxy);
_httpHandlerOptions.MaxConnectionsPerServer.ShouldBe(expected.MaxConnectionsPerServer);
} }
private void GivenARealTracer() private void GivenARealTracer()

View File

@ -1341,6 +1341,32 @@
.BDDfy(); .BDDfy();
} }
[Fact]
public void configuration_is_invalid_when_placeholder_is_used_twice_in_upstream_path_template()
{
this.Given(x => x.GivenAConfiguration(new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/bar/{everything}",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort() { Host = "a.b.cd" },
},
UpstreamPathTemplate = "/foo/bar/{everything}/{everything}",
UpstreamHttpMethod = new List<string> { "Get" },
},
},
}))
.When(x => x.WhenIValidateTheConfiguration())
.Then(x => x.ThenTheResultIsNotValid())
.And(x => x.ThenTheErrorMessageAtPositionIs(0, "reRoute /foo/bar/{everything}/{everything} has duplicated placeholder"))
.BDDfy();
}
private void GivenAConfiguration(FileConfiguration fileConfiguration) private void GivenAConfiguration(FileConfiguration fileConfiguration)
{ {
_fileConfiguration = fileConfiguration; _fileConfiguration = fileConfiguration;

View File

@ -350,6 +350,36 @@
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_not_replace_by_empty_scheme()
{
var downstreamReRoute = new DownstreamReRouteBuilder()
.WithDownstreamScheme("")
.WithServiceName("Ocelot/OcelotApp")
.WithUseServiceDiscovery(true)
.Build();
var downstreamRoute = new DownstreamRoute(
new List<PlaceholderNameAndValue>(),
new ReRouteBuilder()
.WithDownstreamReRoute(downstreamReRoute)
.Build());
var config = new ServiceProviderConfigurationBuilder()
.WithType("ServiceFabric")
.WithHost("localhost")
.WithPort(19081)
.Build();
this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
.And(x => GivenTheServiceProviderConfigIs(config))
.And(x => x.GivenTheDownstreamRequestUriIs("https://localhost:19081?PartitionKind=test&PartitionKey=1"))
.And(x => x.GivenTheUrlReplacerWillReturnSequence("/api/products/1", "Ocelot/OcelotApp"))
.When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheDownstreamRequestUriIs("https://localhost:19081/Ocelot/OcelotApp/api/products/1?PartitionKind=test&PartitionKey=1"))
.BDDfy();
}
private void GivenTheServiceProviderConfigIs(ServiceProviderConfiguration config) private void GivenTheServiceProviderConfigIs(ServiceProviderConfiguration config)
{ {
var configuration = new InternalConfiguration(null, null, config, null, null, null, null, null); var configuration = new InternalConfiguration(null, null, config, null, null, null, null, null);

View File

@ -1,3 +1,5 @@
using System;
using System.Linq.Expressions;
using Ocelot.Middleware; using Ocelot.Middleware;
namespace Ocelot.UnitTests.LoadBalancer namespace Ocelot.UnitTests.LoadBalancer
@ -108,6 +110,26 @@ namespace Ocelot.UnitTests.LoadBalancer
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_set_scheme()
{
var downstreamRoute = new DownstreamReRouteBuilder()
.WithUpstreamHttpMethod(new List<string> { "Get" })
.Build();
var serviceProviderConfig = new ServiceProviderConfigurationBuilder()
.Build();
this.Given(x => x.GivenTheDownStreamUrlIs("http://my.url/abc?q=123"))
.And(x => GivenTheConfigurationIs(serviceProviderConfig))
.And(x => x.GivenTheDownStreamRouteIs(downstreamRoute, new List<Ocelot.DownstreamRouteFinder.UrlMatcher.PlaceholderNameAndValue>()))
.And(x => x.GivenTheLoadBalancerHouseReturns())
.And(x => x.GivenTheLoadBalancerReturnsOk())
.When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenAnHostAndPortIsSetOnPipeline())
.BDDfy();
}
private void WhenICallTheMiddleware() private void WhenICallTheMiddleware()
{ {
_middleware = new LoadBalancingMiddleware(_next, _loggerFactory.Object, _loadBalancerHouse.Object); _middleware = new LoadBalancingMiddleware(_next, _loggerFactory.Object, _loadBalancerHouse.Object);
@ -135,6 +157,13 @@ namespace Ocelot.UnitTests.LoadBalancer
.ReturnsAsync(_getHostAndPortError); .ReturnsAsync(_getHostAndPortError);
} }
private void GivenTheLoadBalancerReturnsOk()
{
_loadBalancer
.Setup(x => x.Lease(It.IsAny<DownstreamContext>()))
.ReturnsAsync(new OkResponse<ServiceHostAndPort>(new ServiceHostAndPort("abc", 123, "https")));
}
private void GivenTheLoadBalancerReturns() private void GivenTheLoadBalancerReturns()
{ {
_hostAndPort = new ServiceHostAndPort("127.0.0.1", 80); _hostAndPort = new ServiceHostAndPort("127.0.0.1", 80);
@ -186,6 +215,13 @@ namespace Ocelot.UnitTests.LoadBalancer
_downstreamContext.Errors.ShouldBe(_getHostAndPortError.Errors); _downstreamContext.Errors.ShouldBe(_getHostAndPortError.Errors);
} }
private void ThenAnHostAndPortIsSetOnPipeline()
{
_downstreamContext.DownstreamRequest.Host.ShouldBeEquivalentTo("abc");
_downstreamContext.DownstreamRequest.Port.ShouldBeEquivalentTo(123);
_downstreamContext.DownstreamRequest.Scheme.ShouldBeEquivalentTo("https");
}
private void ThenTheDownstreamUrlIsReplacedWith(string expectedUri) private void ThenTheDownstreamUrlIsReplacedWith(string expectedUri)
{ {
_downstreamContext.DownstreamRequest.ToHttpRequestMessage().RequestUri.OriginalString.ShouldBe(expectedUri); _downstreamContext.DownstreamRequest.ToHttpRequestMessage().RequestUri.OriginalString.ShouldBe(expectedUri);

View File

@ -52,7 +52,7 @@ namespace Ocelot.UnitTests.Requester
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true)) .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue))
.WithDelegatingHandlers(new List<string> .WithDelegatingHandlers(new List<string>
{ {
"FakeDelegatingHandler", "FakeDelegatingHandler",
@ -88,7 +88,7 @@ namespace Ocelot.UnitTests.Requester
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true)) .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue))
.WithDelegatingHandlers(new List<string> .WithDelegatingHandlers(new List<string>
{ {
"FakeDelegatingHandlerTwo", "FakeDelegatingHandlerTwo",
@ -125,7 +125,7 @@ namespace Ocelot.UnitTests.Requester
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true)) .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue))
.WithDelegatingHandlers(new List<string> .WithDelegatingHandlers(new List<string>
{ {
"FakeDelegatingHandlerTwo", "FakeDelegatingHandlerTwo",
@ -161,7 +161,7 @@ namespace Ocelot.UnitTests.Requester
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true)) .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue))
.WithDelegatingHandlers(new List<string> .WithDelegatingHandlers(new List<string>
{ {
"FakeDelegatingHandler", "FakeDelegatingHandler",
@ -195,7 +195,7 @@ namespace Ocelot.UnitTests.Requester
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true)) .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue))
.WithLoadBalancerKey("") .WithLoadBalancerKey("")
.Build(); .Build();
@ -221,7 +221,7 @@ namespace Ocelot.UnitTests.Requester
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true)) .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true, int.MaxValue))
.WithDelegatingHandlers(new List<string> .WithDelegatingHandlers(new List<string>
{ {
"FakeDelegatingHandler", "FakeDelegatingHandler",
@ -249,7 +249,7 @@ namespace Ocelot.UnitTests.Requester
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true)).WithLoadBalancerKey("").Build(); .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true, int.MaxValue)).WithLoadBalancerKey("").Build();
this.Given(x => GivenTheFollowingRequest(reRoute)) this.Given(x => GivenTheFollowingRequest(reRoute))
.And(x => GivenTheQosFactoryReturns(new FakeQoSHandler())) .And(x => GivenTheQosFactoryReturns(new FakeQoSHandler()))
@ -269,7 +269,7 @@ namespace Ocelot.UnitTests.Requester
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true)).WithLoadBalancerKey("").Build(); .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true, int.MaxValue)).WithLoadBalancerKey("").Build();
this.Given(x => GivenTheFollowingRequest(reRoute)) this.Given(x => GivenTheFollowingRequest(reRoute))
.And(x => GivenTheServiceProviderReturnsNothing()) .And(x => GivenTheServiceProviderReturnsNothing())
@ -289,7 +289,7 @@ namespace Ocelot.UnitTests.Requester
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true)).WithLoadBalancerKey("").Build(); .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true, int.MaxValue)).WithLoadBalancerKey("").Build();
this.Given(x => GivenTheFollowingRequest(reRoute)) this.Given(x => GivenTheFollowingRequest(reRoute))
.And(x => GivenTheQosFactoryReturns(new FakeQoSHandler())) .And(x => GivenTheQosFactoryReturns(new FakeQoSHandler()))
@ -309,7 +309,7 @@ namespace Ocelot.UnitTests.Requester
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true)).WithLoadBalancerKey("").Build(); .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true, int.MaxValue)).WithLoadBalancerKey("").Build();
this.Given(x => GivenTheFollowingRequest(reRoute)) this.Given(x => GivenTheFollowingRequest(reRoute))
.And(x => GivenTheQosFactoryReturns(new FakeQoSHandler())) .And(x => GivenTheQosFactoryReturns(new FakeQoSHandler()))
@ -331,7 +331,7 @@ namespace Ocelot.UnitTests.Requester
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true)) .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue))
.WithLoadBalancerKey("") .WithLoadBalancerKey("")
.Build(); .Build();
@ -361,7 +361,7 @@ namespace Ocelot.UnitTests.Requester
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true)) .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue))
.WithLoadBalancerKey("") .WithLoadBalancerKey("")
.Build(); .Build();

View File

@ -52,7 +52,7 @@ namespace Ocelot.UnitTests.Requester
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true)) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
.WithLoadBalancerKey("") .WithLoadBalancerKey("")
.WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build())
.WithQosOptions(new QoSOptionsBuilder().Build()) .WithQosOptions(new QoSOptionsBuilder().Build())
@ -73,7 +73,7 @@ namespace Ocelot.UnitTests.Requester
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true)) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
.WithLoadBalancerKey("") .WithLoadBalancerKey("")
.WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build())
.WithQosOptions(new QoSOptionsBuilder().Build()) .WithQosOptions(new QoSOptionsBuilder().Build())
@ -99,7 +99,7 @@ namespace Ocelot.UnitTests.Requester
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true)) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
.WithLoadBalancerKey("") .WithLoadBalancerKey("")
.WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build())
.WithQosOptions(new QoSOptionsBuilder().Build()) .WithQosOptions(new QoSOptionsBuilder().Build())
@ -126,7 +126,7 @@ namespace Ocelot.UnitTests.Requester
var reRouteA = new DownstreamReRouteBuilder() var reRouteA = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true)) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
.WithLoadBalancerKey("") .WithLoadBalancerKey("")
.WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithContainsQueryString(true).WithOriginalValue("").Build()) .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithContainsQueryString(true).WithOriginalValue("").Build())
.WithQosOptions(new QoSOptionsBuilder().Build()) .WithQosOptions(new QoSOptionsBuilder().Build())
@ -134,7 +134,7 @@ namespace Ocelot.UnitTests.Requester
var reRouteB = new DownstreamReRouteBuilder() var reRouteB = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true)) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
.WithLoadBalancerKey("") .WithLoadBalancerKey("")
.WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithContainsQueryString(true).WithOriginalValue("").Build()) .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithContainsQueryString(true).WithOriginalValue("").Build())
.WithQosOptions(new QoSOptionsBuilder().Build()) .WithQosOptions(new QoSOptionsBuilder().Build())
@ -161,7 +161,7 @@ namespace Ocelot.UnitTests.Requester
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true)) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
.WithLoadBalancerKey("") .WithLoadBalancerKey("")
.WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build())
.WithQosOptions(new QoSOptionsBuilder().Build()) .WithQosOptions(new QoSOptionsBuilder().Build())
@ -184,7 +184,7 @@ namespace Ocelot.UnitTests.Requester
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true)) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
.WithLoadBalancerKey("") .WithLoadBalancerKey("")
.WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build())
.WithQosOptions(new QoSOptionsBuilder().Build()) .WithQosOptions(new QoSOptionsBuilder().Build())
@ -216,7 +216,7 @@ namespace Ocelot.UnitTests.Requester
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(false, true, false, true)) .WithHttpHandlerOptions(new HttpHandlerOptions(false, true, false, true, int.MaxValue))
.WithLoadBalancerKey("") .WithLoadBalancerKey("")
.WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build())
.WithQosOptions(new QoSOptionsBuilder().Build()) .WithQosOptions(new QoSOptionsBuilder().Build())
@ -252,7 +252,7 @@ namespace Ocelot.UnitTests.Requester
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true)) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
.WithLoadBalancerKey("") .WithLoadBalancerKey("")
.WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build())
.WithQosOptions(new QoSOptionsBuilder().Build()) .WithQosOptions(new QoSOptionsBuilder().Build())

View File

@ -57,7 +57,7 @@ namespace Ocelot.UnitTests.Requester
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true)) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
.WithLoadBalancerKey("") .WithLoadBalancerKey("")
.WithUpstreamPathTemplate(upstreamTemplate) .WithUpstreamPathTemplate(upstreamTemplate)
.WithQosOptions(new QoSOptionsBuilder().Build()) .WithQosOptions(new QoSOptionsBuilder().Build())
@ -86,7 +86,7 @@ namespace Ocelot.UnitTests.Requester
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true)) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
.WithLoadBalancerKey("") .WithLoadBalancerKey("")
.WithUpstreamPathTemplate(upstreamTemplate) .WithUpstreamPathTemplate(upstreamTemplate)
.WithQosOptions(new QoSOptionsBuilder().Build()) .WithQosOptions(new QoSOptionsBuilder().Build())
@ -114,7 +114,7 @@ namespace Ocelot.UnitTests.Requester
var reRoute = new DownstreamReRouteBuilder() var reRoute = new DownstreamReRouteBuilder()
.WithQosOptions(qosOptions) .WithQosOptions(qosOptions)
.WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true)) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue))
.WithLoadBalancerKey("") .WithLoadBalancerKey("")
.WithUpstreamPathTemplate(upstreamTemplate) .WithUpstreamPathTemplate(upstreamTemplate)
.WithQosOptions(new QoSOptionsBuilder().WithTimeoutValue(1).Build()) .WithQosOptions(new QoSOptionsBuilder().WithTimeoutValue(1).Build())