Skip to content

Commit

Permalink
Make Ocelot not add forward slash to downstream url (#158)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
TomPallister committed Nov 19, 2017
1 parent 6824210 commit 9ba57f8
Show file tree
Hide file tree
Showing 12 changed files with 86 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
using Ocelot.Logging;
using Ocelot.Requester.QoS;
using Ocelot.Responses;
using Ocelot.Utilities;

namespace Ocelot.Configuration.Creator
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
using System.Collections.Generic;
using Ocelot.Configuration.File;
using Ocelot.Utilities;

namespace Ocelot.Configuration.Creator
{
public class UpstreamTemplatePatternCreator : IUpstreamTemplatePatternCreator
{
private const string RegExMatchEverything = ".*";
private const string RegExMatchEverything = "[0-9a-zA-Z].*";
private const string RegExMatchEndString = "$";
private const string RegExIgnoreCase = "(?i)";
private const string RegExForwardSlashOnly = "^/$";
Expand All @@ -15,8 +14,6 @@ public string Create(FileReRoute reRoute)
{
var upstreamTemplate = reRoute.UpstreamPathTemplate;

upstreamTemplate = upstreamTemplate.SetLastCharacterAs('/');

var placeholders = new List<string>();

for (var i = 0; i < upstreamTemplate.Length; i++)
Expand All @@ -40,6 +37,11 @@ public string Create(FileReRoute reRoute)
return RegExForwardSlashOnly;
}

if(upstreamTemplate.EndsWith("/"))
{
upstreamTemplate = upstreamTemplate.Remove(upstreamTemplate.Length -1, 1) + "(/|)";
}

var route = reRoute.ReRouteIsCaseSensitive
? $"^{upstreamTemplate}{RegExMatchEndString}"
: $"^{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Errors;
using Ocelot.Responses;
using Ocelot.Utilities;

namespace Ocelot.DownstreamRouteFinder.Finder
{
Expand All @@ -24,8 +23,6 @@ public DownstreamRouteFinder(IUrlPathToUrlTemplateMatcher urlMatcher, IUrlPathPl

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()));

foreach (var reRoute in applicableReRoutes)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging;
using Ocelot.Middleware;
using Ocelot.Utilities;

namespace Ocelot.DownstreamRouteFinder.Middleware
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public Response<List<UrlPathPlaceholderNameAndValue>> Find(string upstreamUrlPat

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]))
{
Expand Down
17 changes: 0 additions & 17 deletions src/Ocelot/Utilities/StringExtensions.cs

This file was deleted.

44 changes: 39 additions & 5 deletions test/Ocelot.AcceptanceTests/RoutingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ public void should_return_response_200_when_host_has_trailing_slash()
}

[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
{
Expand Down Expand Up @@ -187,7 +187,7 @@ public void should_not_care_about_no_trailing()
}

[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
{
Expand All @@ -209,8 +209,7 @@ public void should_not_care_about_trailing()
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound))
.BDDfy();
}

Expand Down Expand Up @@ -483,6 +482,41 @@ public void should_return_404_when_calling_upstream_route_with_no_matching_downs
.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)
{
_builder = new WebHostBuilder()
Expand All @@ -495,7 +529,7 @@ private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int
app.UsePathBase(basePath);
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;
await context.Response.WriteAsync(responseBody);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,23 @@ public void should_set_upstream_template_pattern_to_ignore_case_sensitivity()

this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
.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();
}

Expand All @@ -42,7 +58,7 @@ public void should_set_upstream_template_pattern_to_respect_case_sensitivity()
};
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
.When(x => x.WhenICreateTheTemplatePattern())
.Then(x => x.ThenTheFollowingIsReturned("^/PRODUCTS/.*/$"))
.Then(x => x.ThenTheFollowingIsReturned("^/PRODUCTS/[0-9a-zA-Z].*$"))
.BDDfy();
}

Expand All @@ -57,7 +73,7 @@ public void should_create_template_pattern_that_matches_anything_to_end_of_strin

this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
.When(x => x.WhenICreateTheTemplatePattern())
.Then(x => x.ThenTheFollowingIsReturned("^/api/products/.*/$"))
.Then(x => x.ThenTheFollowingIsReturned("^/api/products/[0-9a-zA-Z].*$"))
.BDDfy();
}

Expand All @@ -72,9 +88,10 @@ public void should_create_template_pattern_that_matches_more_than_one_placeholde

this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
.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();
}

[Fact]
public void should_create_template_pattern_that_matches_more_than_one_placeholder_with_trailing_slash()
{
Expand All @@ -86,7 +103,7 @@ public void should_create_template_pattern_that_matches_more_than_one_placeholde

this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
.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();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public void should_return_route()


[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();

Expand Down Expand Up @@ -97,7 +97,7 @@ public void should_append_slash_to_upstream_url_path()
.WithUpstreamHttpMethod(new List<string> { "Get" })
.Build()
)))
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly("matchInUrlMatcher/"))
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly("matchInUrlMatcher"))
.BDDfy();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ public void can_match_down_stream_url()
.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]
public void can_match_down_stream_url_with_no_slash()
{
Expand Down
6 changes: 6 additions & 0 deletions test/Ocelot.UnitTests/Ocelot.UnitTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>

<ItemGroup>
<None Update="idsrv3test.pfx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.0.0" />
Expand Down
Binary file added test/Ocelot.UnitTests/idsrv3test.pfx
Binary file not shown.

0 comments on commit 9ba57f8

Please sign in to comment.