Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 27 additions & 6 deletions src/Geta.404Handler/Core/RequestHandler.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Geta Digital. All rights reserved.
// Copyright (c) Geta Digital. All rights reserved.
// Licensed under Apache-2.0. See the LICENSE file in the project root for more information

using System;
Expand All @@ -21,6 +21,7 @@ public class RequestHandler
private readonly IConfiguration _configuration;
private const string HandledRequestItemKey = "404handler:handled";

private const string RedirectedOnceKey = "404handler:redirected";
private static readonly ILogger Logger = LogManager.GetLogger();

public RequestHandler(
Expand Down Expand Up @@ -86,10 +87,18 @@ public virtual void Handle(HttpContextBase context)
return;
}

if (context.Items.Contains(RedirectedOnceKey))
{
LogDebug("Redirect already attempted in this request, skipping to avoid loop.", context);
return;
}

var canHandleRedirect = HandleRequest(context.Request.UrlReferrer, notFoundUri, out var newUrl);
if (canHandleRedirect && newUrl.State == (int)RedirectState.Saved)
{
LogDebug("Handled saved URL", context);

context.Items[RedirectedOnceKey] = true;

context
.ClearServerError()
Expand Down Expand Up @@ -140,14 +149,26 @@ public virtual bool HandleRequest(Uri referrer, Uri urlNotFound, out CustomRedir

if (redirect.State.Equals((int)RedirectState.Saved))
{
// Found it, however, we need to make sure we're not running in an
// infinite loop. The new url must not be the referrer to this page
if (string.Compare(redirect.NewUrl, urlNotFound.PathAndQuery, StringComparison.InvariantCultureIgnoreCase) != 0)
// Prevent redirect loop
var currentPath = urlNotFound.PathAndQuery;
var newPath = redirect.NewUrl;

if (string.Equals(newPath, currentPath, StringComparison.OrdinalIgnoreCase))
{
Logger.Debug($"Redirect leads back to the same path: {newPath}. Skipping to avoid loop.");
return false;
}

foundRedirect = redirect;
return true;
// Avoid redirecting back to the referrer (loop possibility)
if (referrer != null &&
string.Equals(referrer.PathAndQuery, newPath, StringComparison.OrdinalIgnoreCase))
{
Logger.Debug($"Redirect target matches referrer: {newPath}. Skipping to avoid loop.");
return false;
}

foundRedirect = redirect;
return true;
}
}
else
Expand Down
26 changes: 24 additions & 2 deletions tests/Geta.404Handler.Tests/RequestHandlerTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Web;
using BVNetwork.NotFound.Core;
using BVNetwork.NotFound.Core.Configuration;
Expand Down Expand Up @@ -226,6 +226,28 @@ public void HandleRequest_returns_false_when_redirect_is_same_as_not_found()
Assert.False(actual);
}

[Fact]
public void HandleRequest_returns_false_when_redirect_matches_referrer()
{
// Arrange
var notFoundUri = new Uri("http://example.com/missing-page");
var referrerUri = new Uri("http://example.com/redirect-target");

var redirect = new CustomRedirect(notFoundUri.ToString(), (int)RedirectState.Saved, 1)
{
NewUrl = referrerUri.PathAndQuery
};

WhenRedirectFound(redirect);

// Act
var actual = _sut.HandleRequest(referrerUri, notFoundUri, out var foundRedirect);

// Assert
Assert.False(actual);
Assert.Null(foundRedirect);
}

private void WhenRedirectFound(CustomRedirect redirect)
{
A.CallTo(() => _redirectHandler.Find(A<Uri>._)).Returns(redirect);
Expand Down Expand Up @@ -338,4 +360,4 @@ private void AssertNotHandled(HttpContextBase context)
Assert.False(context.Response.TrySkipIisCustomErrors);
}
}
}
}