mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 18:22:49 +08:00
Make Ocelot not add forward slash to downstream url (#158)
* removed code where we add a trailing slash as this means if we request /1.txt/ instead of /1.txt then some servers will not return the resource at /1.txt. After reading up it seems if you dont have a trailing slash then its a file, if you do then its a resource * test for 145 * removed unused code * fix broken build..my bad
This commit is contained in:
parent
68242102d8
commit
9ba57f8ba6
@ -14,7 +14,6 @@ using Ocelot.LoadBalancer.LoadBalancers;
|
|||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Requester.QoS;
|
using Ocelot.Requester.QoS;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using Ocelot.Utilities;
|
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Creator
|
namespace Ocelot.Configuration.Creator
|
||||||
{
|
{
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
using Ocelot.Utilities;
|
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Creator
|
namespace Ocelot.Configuration.Creator
|
||||||
{
|
{
|
||||||
public class UpstreamTemplatePatternCreator : IUpstreamTemplatePatternCreator
|
public class UpstreamTemplatePatternCreator : IUpstreamTemplatePatternCreator
|
||||||
{
|
{
|
||||||
private const string RegExMatchEverything = ".*";
|
private const string RegExMatchEverything = "[0-9a-zA-Z].*";
|
||||||
private const string RegExMatchEndString = "$";
|
private const string RegExMatchEndString = "$";
|
||||||
private const string RegExIgnoreCase = "(?i)";
|
private const string RegExIgnoreCase = "(?i)";
|
||||||
private const string RegExForwardSlashOnly = "^/$";
|
private const string RegExForwardSlashOnly = "^/$";
|
||||||
@ -15,8 +14,6 @@ namespace Ocelot.Configuration.Creator
|
|||||||
{
|
{
|
||||||
var upstreamTemplate = reRoute.UpstreamPathTemplate;
|
var upstreamTemplate = reRoute.UpstreamPathTemplate;
|
||||||
|
|
||||||
upstreamTemplate = upstreamTemplate.SetLastCharacterAs('/');
|
|
||||||
|
|
||||||
var placeholders = new List<string>();
|
var placeholders = new List<string>();
|
||||||
|
|
||||||
for (var i = 0; i < upstreamTemplate.Length; i++)
|
for (var i = 0; i < upstreamTemplate.Length; i++)
|
||||||
@ -40,6 +37,11 @@ namespace Ocelot.Configuration.Creator
|
|||||||
return RegExForwardSlashOnly;
|
return RegExForwardSlashOnly;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(upstreamTemplate.EndsWith("/"))
|
||||||
|
{
|
||||||
|
upstreamTemplate = upstreamTemplate.Remove(upstreamTemplate.Length -1, 1) + "(/|)";
|
||||||
|
}
|
||||||
|
|
||||||
var route = reRoute.ReRouteIsCaseSensitive
|
var route = reRoute.ReRouteIsCaseSensitive
|
||||||
? $"^{upstreamTemplate}{RegExMatchEndString}"
|
? $"^{upstreamTemplate}{RegExMatchEndString}"
|
||||||
: $"^{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}";
|
: $"^{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}";
|
||||||
|
@ -7,7 +7,6 @@ using Ocelot.Configuration.Provider;
|
|||||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||||
using Ocelot.Errors;
|
using Ocelot.Errors;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using Ocelot.Utilities;
|
|
||||||
|
|
||||||
namespace Ocelot.DownstreamRouteFinder.Finder
|
namespace Ocelot.DownstreamRouteFinder.Finder
|
||||||
{
|
{
|
||||||
@ -24,8 +23,6 @@ namespace Ocelot.DownstreamRouteFinder.Finder
|
|||||||
|
|
||||||
public Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod, IOcelotConfiguration configuration)
|
public Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod, IOcelotConfiguration configuration)
|
||||||
{
|
{
|
||||||
upstreamUrlPath = upstreamUrlPath.SetLastCharacterAs('/');
|
|
||||||
|
|
||||||
var applicableReRoutes = configuration.ReRoutes.Where(r => r.UpstreamHttpMethod.Count == 0 || r.UpstreamHttpMethod.Select(x => x.Method.ToLower()).Contains(upstreamHttpMethod.ToLower()));
|
var applicableReRoutes = configuration.ReRoutes.Where(r => r.UpstreamHttpMethod.Count == 0 || r.UpstreamHttpMethod.Select(x => x.Method.ToLower()).Contains(upstreamHttpMethod.ToLower()));
|
||||||
|
|
||||||
foreach (var reRoute in applicableReRoutes)
|
foreach (var reRoute in applicableReRoutes)
|
||||||
|
@ -8,7 +8,6 @@ using Ocelot.Infrastructure.Extensions;
|
|||||||
using Ocelot.Infrastructure.RequestData;
|
using Ocelot.Infrastructure.RequestData;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
using Ocelot.Utilities;
|
|
||||||
|
|
||||||
namespace Ocelot.DownstreamRouteFinder.Middleware
|
namespace Ocelot.DownstreamRouteFinder.Middleware
|
||||||
{
|
{
|
||||||
|
@ -13,7 +13,7 @@ namespace Ocelot.DownstreamRouteFinder.UrlMatcher
|
|||||||
|
|
||||||
for (int counterForTemplate = 0; counterForTemplate < upstreamUrlPathTemplate.Length; counterForTemplate++)
|
for (int counterForTemplate = 0; counterForTemplate < upstreamUrlPathTemplate.Length; counterForTemplate++)
|
||||||
{
|
{
|
||||||
if (CharactersDontMatch(upstreamUrlPathTemplate[counterForTemplate], upstreamUrlPath[counterForUrl]) && ContinueScanningUrl(counterForUrl,upstreamUrlPath.Length))
|
if ((upstreamUrlPath.Length > counterForUrl) && CharactersDontMatch(upstreamUrlPathTemplate[counterForTemplate], upstreamUrlPath[counterForUrl]) && ContinueScanningUrl(counterForUrl,upstreamUrlPath.Length))
|
||||||
{
|
{
|
||||||
if (IsPlaceholder(upstreamUrlPathTemplate[counterForTemplate]))
|
if (IsPlaceholder(upstreamUrlPathTemplate[counterForTemplate]))
|
||||||
{
|
{
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
namespace Ocelot.Utilities
|
|
||||||
{
|
|
||||||
public static class StringExtensions
|
|
||||||
{
|
|
||||||
public static string SetLastCharacterAs(this string valueToSetLastChar,
|
|
||||||
char expectedLastChar)
|
|
||||||
{
|
|
||||||
var last = valueToSetLastChar[valueToSetLastChar.Length - 1];
|
|
||||||
|
|
||||||
if (last != expectedLastChar)
|
|
||||||
{
|
|
||||||
valueToSetLastChar = $"{valueToSetLastChar}{expectedLastChar}";
|
|
||||||
}
|
|
||||||
return valueToSetLastChar;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -159,7 +159,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_not_care_about_no_trailing()
|
public void should_return_ok_when_upstream_url_ends_with_forward_slash_but_template_does_not()
|
||||||
{
|
{
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
@ -187,7 +187,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_not_care_about_trailing()
|
public void should_return_not_found_when_upstream_url_ends_with_forward_slash_but_template_does_not()
|
||||||
{
|
{
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
@ -209,8 +209,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunning())
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/"))
|
||||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound))
|
||||||
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -483,6 +482,41 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_fix_145()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/api/{url}",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHost = "localhost",
|
||||||
|
DownstreamPort = 51899,
|
||||||
|
UpstreamPathTemplate = "/platform/{url}",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
QoSOptions = new FileQoSOptions {
|
||||||
|
ExceptionsAllowedBeforeBreaking = 3,
|
||||||
|
DurationOfBreak = 10,
|
||||||
|
TimeoutValue = 5000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51899", "", 200, "Hello from Laura"))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/platform/swagger/lib/backbone-min.js"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
|
.And(x => ThenTheDownstreamUrlPathShouldBe("/api/swagger/lib/backbone-min.js"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
|
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
|
||||||
{
|
{
|
||||||
_builder = new WebHostBuilder()
|
_builder = new WebHostBuilder()
|
||||||
@ -495,7 +529,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
app.UsePathBase(basePath);
|
app.UsePathBase(basePath);
|
||||||
app.Run(async context =>
|
app.Run(async context =>
|
||||||
{
|
{
|
||||||
_downstreamPath = context.Request.PathBase.Value;
|
_downstreamPath = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
|
||||||
context.Response.StatusCode = statusCode;
|
context.Response.StatusCode = statusCode;
|
||||||
await context.Response.WriteAsync(responseBody);
|
await context.Response.WriteAsync(responseBody);
|
||||||
});
|
});
|
||||||
|
@ -28,7 +28,23 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
|
|
||||||
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
|
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
|
||||||
.When(x => x.WhenICreateTheTemplatePattern())
|
.When(x => x.WhenICreateTheTemplatePattern())
|
||||||
.Then(x => x.ThenTheFollowingIsReturned("^(?i)/PRODUCTS/.*/$"))
|
.Then(x => x.ThenTheFollowingIsReturned("^(?i)/PRODUCTS/[0-9a-zA-Z].*$"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_match_forward_slash_or_no_forward_slash_if_template_end_with_forward_slash()
|
||||||
|
{
|
||||||
|
var fileReRoute = new FileReRoute
|
||||||
|
{
|
||||||
|
UpstreamPathTemplate = "/PRODUCTS/",
|
||||||
|
ReRouteIsCaseSensitive = false
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
|
||||||
|
.When(x => x.WhenICreateTheTemplatePattern())
|
||||||
|
.Then(x => x.ThenTheFollowingIsReturned("^(?i)/PRODUCTS(/|)$"))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +58,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
};
|
};
|
||||||
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
|
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
|
||||||
.When(x => x.WhenICreateTheTemplatePattern())
|
.When(x => x.WhenICreateTheTemplatePattern())
|
||||||
.Then(x => x.ThenTheFollowingIsReturned("^/PRODUCTS/.*/$"))
|
.Then(x => x.ThenTheFollowingIsReturned("^/PRODUCTS/[0-9a-zA-Z].*$"))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +73,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
|
|
||||||
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
|
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
|
||||||
.When(x => x.WhenICreateTheTemplatePattern())
|
.When(x => x.WhenICreateTheTemplatePattern())
|
||||||
.Then(x => x.ThenTheFollowingIsReturned("^/api/products/.*/$"))
|
.Then(x => x.ThenTheFollowingIsReturned("^/api/products/[0-9a-zA-Z].*$"))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,9 +88,10 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
|
|
||||||
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
|
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
|
||||||
.When(x => x.WhenICreateTheTemplatePattern())
|
.When(x => x.WhenICreateTheTemplatePattern())
|
||||||
.Then(x => x.ThenTheFollowingIsReturned("^/api/products/.*/variants/.*/$"))
|
.Then(x => x.ThenTheFollowingIsReturned("^/api/products/[0-9a-zA-Z].*/variants/[0-9a-zA-Z].*$"))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_create_template_pattern_that_matches_more_than_one_placeholder_with_trailing_slash()
|
public void should_create_template_pattern_that_matches_more_than_one_placeholder_with_trailing_slash()
|
||||||
{
|
{
|
||||||
@ -86,7 +103,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
|
|
||||||
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
|
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
|
||||||
.When(x => x.WhenICreateTheTemplatePattern())
|
.When(x => x.WhenICreateTheTemplatePattern())
|
||||||
.Then(x => x.ThenTheFollowingIsReturned("^/api/products/.*/variants/.*/$"))
|
.Then(x => x.ThenTheFollowingIsReturned("^/api/products/[0-9a-zA-Z].*/variants/[0-9a-zA-Z].*(/|)$"))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_append_slash_to_upstream_url_path()
|
public void should_not_append_slash_to_upstream_url_path()
|
||||||
{
|
{
|
||||||
var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build();
|
var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build();
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||||
.Build()
|
.Build()
|
||||||
)))
|
)))
|
||||||
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly("matchInUrlMatcher/"))
|
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly("matchInUrlMatcher"))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +30,16 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
|
|||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_not_find_anything()
|
||||||
|
{
|
||||||
|
this.Given(x => x.GivenIHaveAUpstreamPath("/products"))
|
||||||
|
.And(x => x.GivenIHaveAnUpstreamUrlTemplate("/products/"))
|
||||||
|
.When(x => x.WhenIFindTheUrlVariableNamesAndValues())
|
||||||
|
.And(x => x.ThenTheTemplatesVariablesAre(new List<UrlPathPlaceholderNameAndValue>()))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void can_match_down_stream_url_with_no_slash()
|
public void can_match_down_stream_url_with_no_slash()
|
||||||
{
|
{
|
||||||
|
@ -31,6 +31,12 @@
|
|||||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="idsrv3test.pfx">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
|
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.0.0" />
|
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.0.0" />
|
||||||
|
BIN
test/Ocelot.UnitTests/idsrv3test.pfx
Normal file
BIN
test/Ocelot.UnitTests/idsrv3test.pfx
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user