Moves RoutableDocumentFilter along with tests

This commit is contained in:
Shannon
2021-01-08 15:27:07 +11:00
parent ee1663c978
commit f8033c5281
10 changed files with 315 additions and 303 deletions

View File

@@ -0,0 +1,112 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Patterns;
using Moq;
using NUnit.Framework;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.Hosting;
using Umbraco.Core.IO;
using Umbraco.Web.Common.Routing;
namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.Routing
{
[TestFixture]
public class RoutableDocumentFilterTests
{
private GlobalSettings GetGlobalSettings() => new GlobalSettings();
private IHostingEnvironment GetHostingEnvironment()
{
var hostingEnv = new Mock<IHostingEnvironment>();
hostingEnv.Setup(x => x.ToAbsolute(It.IsAny<string>())).Returns((string virtualPath) => virtualPath.TrimStart('~', '/'));
return hostingEnv.Object;
}
[TestCase("/umbraco/editContent.aspx")]
[TestCase("/install/default.aspx")]
[TestCase("/install/")]
[TestCase("/install")]
[TestCase("/install/?installStep=asdf")]
[TestCase("/install/test.aspx")]
public void Is_Reserved_Path_Or_Url(string url)
{
var routableDocFilter = new RoutableDocumentFilter(
GetGlobalSettings(),
GetHostingEnvironment(),
new DefaultEndpointDataSource());
// Will be false if it is a reserved path
Assert.IsFalse(routableDocFilter.IsDocumentRequest(url));
}
[TestCase("/base/somebasehandler")]
[TestCase("/")]
[TestCase("/home.aspx")]
[TestCase("/umbraco-test")]
[TestCase("/install-test")]
[TestCase("/install.aspx")]
public void Is_Not_Reserved_Path_Or_Url(string url)
{
var routableDocFilter = new RoutableDocumentFilter(
GetGlobalSettings(),
GetHostingEnvironment(),
new DefaultEndpointDataSource());
// Will be true if it's not reserved
Assert.IsTrue(routableDocFilter.IsDocumentRequest(url));
}
[TestCase("/Do/Not/match", false)]
[TestCase("/Umbraco/RenderMvcs", false)]
[TestCase("/Umbraco/RenderMvc", true)]
[TestCase("/umbraco/RenderMvc/Index", true)]
[TestCase("/Umbraco/RenderMvc/Index/1234", true)]
[TestCase("/Umbraco/RenderMvc/Index/1234/", true)]
[TestCase("/Umbraco/RenderMvc/Index/1234/9876", false)]
[TestCase("/api", true)]
[TestCase("/api/WebApiTest", true)]
[TestCase("/Api/WebApiTest/1234", true)]
[TestCase("/api/WebApiTest/Index/1234", false)]
public void Is_Reserved_By_Route(string url, bool isReserved)
{
var globalSettings = new GlobalSettings { ReservedPaths = string.Empty, ReservedUrls = string.Empty };
RouteEndpoint endpoint1 = CreateEndpoint(
"Umbraco/RenderMvc/{action?}/{id?}",
new { controller = "RenderMvc" },
"Umbraco_default",
0);
RouteEndpoint endpoint2 = CreateEndpoint(
"api/{controller?}/{id?}",
new { action = "Index" },
"WebAPI",
1);
var endpointDataSource = new DefaultEndpointDataSource(endpoint1, endpoint2);
var routableDocFilter = new RoutableDocumentFilter(
globalSettings,
GetHostingEnvironment(),
endpointDataSource);
Assert.AreEqual(
!isReserved, // not reserved if it's a document request
routableDocFilter.IsDocumentRequest(url));
}
// borrowed from https://github.com/dotnet/aspnetcore/blob/19559e73da2b6d335b864ed2855dd8a0c7a207a0/src/Mvc/Mvc.Core/test/Routing/ControllerLinkGeneratorExtensionsTest.cs#L171
private RouteEndpoint CreateEndpoint(
string template,
object defaults = null,
string name = null,
int order = 0) => new RouteEndpoint(
(httpContext) => Task.CompletedTask,
RoutePatternFactory.Parse(template, defaults, null),
order,
new EndpointMetadataCollection(Array.Empty<object>()),
name);
}
}

View File

@@ -1,79 +0,0 @@
using System;
using System.Web.Mvc;
using System.Web.Routing;
using NUnit.Framework;
using Umbraco.Core.Configuration.Models;
using Umbraco.Tests.Common.Builders;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web;
namespace Umbraco.Tests.Routing
{
[TestFixture]
public class RoutableDocumentFilterTests : BaseWebTest
{
[TestCase("/umbraco/editContent.aspx")]
[TestCase("/install/default.aspx")]
[TestCase("/install/")]
[TestCase("/install")]
[TestCase("/install/?installStep=asdf")]
[TestCase("/install/test.aspx")]
public void Is_Reserved_Path_Or_Url(string url)
{
var globalSettings = TestObjects.GetGlobalSettings();
var routableDocFilter = new RoutableDocumentFilter(globalSettings, IOHelper);
Assert.IsTrue(routableDocFilter.IsReservedPathOrUrl(url));
}
[TestCase("/base/somebasehandler")]
[TestCase("/")]
[TestCase("/home.aspx")]
[TestCase("/umbraco-test")]
[TestCase("/install-test")]
[TestCase("/install.aspx")]
public void Is_Not_Reserved_Path_Or_Url(string url)
{
var globalSettings = TestObjects.GetGlobalSettings();
var routableDocFilter = new RoutableDocumentFilter(globalSettings, IOHelper);
Assert.IsFalse(routableDocFilter.IsReservedPathOrUrl(url));
}
[TestCase("/Do/Not/match", false)]
[TestCase("/Umbraco/RenderMvcs", false)]
[TestCase("/Umbraco/RenderMvc", true)]
[TestCase("/Umbraco/RenderMvc/Index", true)]
[TestCase("/Umbraco/RenderMvc/Index/1234", true)]
[TestCase("/Umbraco/RenderMvc/Index/1234/9876", false)]
[TestCase("/api", true)]
[TestCase("/api/WebApiTest", true)]
[TestCase("/api/WebApiTest/1234", true)]
[TestCase("/api/WebApiTest/Index/1234", false)]
public void Is_Reserved_By_Route(string url, bool shouldMatch)
{
//reset the app config, we only want to test routes not the hard coded paths
var globalSettings = new GlobalSettings { ReservedPaths = string.Empty, ReservedUrls = string.Empty };
var routableDocFilter = new RoutableDocumentFilter(globalSettings, IOHelper);
var routes = new RouteCollection();
routes.MapRoute(
"Umbraco_default",
"Umbraco/RenderMvc/{action}/{id}",
new { controller = "RenderMvc", action = "Index", id = UrlParameter.Optional });
routes.MapRoute(
"WebAPI",
"api/{controller}/{id}",
new { controller = "WebApiTestController", action = "Index", id = UrlParameter.Optional });
var context = new FakeHttpContextFactory(url);
Assert.AreEqual(
shouldMatch,
routableDocFilter.IsReservedPathOrUrl(url, context.HttpContext, routes));
}
}
}

View File

@@ -34,7 +34,6 @@ namespace Umbraco.Tests.Routing
logger,
null, // FIXME: PublishedRouter complexities...
Mock.Of<IUmbracoContextFactory>(),
new RoutableDocumentFilter(globalSettings, IOHelper),
globalSettings,
HostingEnvironment
);

View File

@@ -153,7 +153,6 @@
<Compile Include="PublishedContent\PublishedContentSnapshotTestBase.cs" />
<Compile Include="PublishedContent\NuCacheTests.cs" />
<Compile Include="Routing\MediaUrlProviderTests.cs" />
<Compile Include="Routing\RoutableDocumentFilterTests.cs" />
<Compile Include="Routing\GetContentUrlsTests.cs" />
<Compile Include="TestHelpers\RandomIdRamDirectory.cs" />
<Compile Include="TestHelpers\Stubs\TestUserPasswordConfig.cs" />

View File

@@ -0,0 +1,194 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Template;
using Umbraco.Core;
using Umbraco.Core.Collections;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.Hosting;
using Umbraco.Core.IO;
namespace Umbraco.Web.Common.Routing
{
/// <summary>
/// Utility class used to check if the current request is for a front-end request
/// </summary>
/// <remarks>
/// There are various checks to determine if this is a front-end request such as checking if the request is part of any reserved paths or existing MVC routes.
/// </remarks>
public sealed class RoutableDocumentFilter
{
private readonly ConcurrentDictionary<string, bool> _routeChecks = new ConcurrentDictionary<string, bool>();
private readonly GlobalSettings _globalSettings;
private readonly IHostingEnvironment _hostingEnvironment;
private readonly EndpointDataSource _endpointDataSource;
private readonly object _routeLocker = new object();
#pragma warning disable IDE0044 // Add readonly modifier
private object _initLocker = new object();
private bool _isInit = false;
private HashSet<string> _reservedList;
#pragma warning restore IDE0044 // Add readonly modifier
/// <summary>
/// Initializes a new instance of the <see cref="RoutableDocumentFilter"/> class.
/// </summary>
public RoutableDocumentFilter(GlobalSettings globalSettings, IHostingEnvironment hostingEnvironment, EndpointDataSource endpointDataSource)
{
_globalSettings = globalSettings;
_hostingEnvironment = hostingEnvironment;
_endpointDataSource = endpointDataSource;
_endpointDataSource.GetChangeToken().RegisterChangeCallback(EndpointsChanged, null);
}
private void EndpointsChanged(object value)
{
lock (_routeLocker)
{
// try clearing each entry
foreach (var r in _routeChecks.Keys.ToList())
{
_routeChecks.TryRemove(r, out _);
}
// re-register after it has changed so we keep listening
_endpointDataSource.GetChangeToken().RegisterChangeCallback(EndpointsChanged, null);
}
}
/// <summary>
/// Checks if the request is a document request (i.e. one that the module should handle)
/// </summary>
public bool IsDocumentRequest(string absPath)
{
var maybeDoc = true;
// a document request should be
// /foo/bar/nil
// /foo/bar/nil/
// /foo/bar/nil.aspx
// where /foo is not a reserved path
// TODO: Remove aspx checks
// if the path contains an extension that is not .aspx
// then it cannot be a document request
var extension = Path.GetExtension(absPath);
if (maybeDoc && extension.IsNullOrWhiteSpace() == false && !extension.InvariantEquals(".aspx"))
{
maybeDoc = false;
}
// at that point, either we have no extension, or it is .aspx
// if the path is reserved then it cannot be a document request
if (maybeDoc && IsReservedPathOrUrl(absPath))
{
maybeDoc = false;
}
return maybeDoc;
}
/// <summary>
/// Determines whether the specified URL is reserved or is inside a reserved path.
/// </summary>
/// <param name="absPath">The Path of the URL to check.</param>
/// <returns>
/// <c>true</c> if the specified URL is reserved; otherwise, <c>false</c>.
/// </returns>
private bool IsReservedPathOrUrl(string absPath)
{
LazyInitializer.EnsureInitialized(ref _reservedList, ref _isInit, ref _initLocker, () =>
{
// store references to strings to determine changes
var reservedPathsCache = _globalSettings.ReservedPaths;
var reservedUrlsCache = _globalSettings.ReservedUrls;
// add URLs and paths to a new list
var newReservedList = new HashSet<string>();
foreach (var reservedUrlTrimmed in NormalizePaths(reservedUrlsCache, false))
{
newReservedList.Add(reservedUrlTrimmed);
}
foreach (var reservedPathTrimmed in NormalizePaths(reservedPathsCache, true))
{
newReservedList.Add(reservedPathTrimmed);
}
// use the new list from now on
return newReservedList;
});
// The URL should be cleaned up before checking:
// * If it doesn't contain an '.' in the path then we assume it is a path based URL, if that is the case we should add an trailing '/' because all of our reservedPaths use a trailing '/'
// * We shouldn't be comparing the query at all
if (absPath.Contains('?'))
{
absPath = absPath.Split('?', StringSplitOptions.RemoveEmptyEntries)[0];
}
if (absPath.Contains('.') == false)
{
absPath = absPath.EnsureEndsWith('/');
}
// return true if URL starts with an element of the reserved list
var isReserved = _reservedList.Any(x => absPath.InvariantStartsWith(x));
if (isReserved)
{
return true;
}
// check if the current request matches a route, if so then it is reserved.
var hasRoute = _routeChecks.GetOrAdd(absPath, x => MatchesEndpoint(absPath));
if (hasRoute)
{
return true;
}
return false;
}
private IEnumerable<string> NormalizePaths(string paths, bool ensureTrailingSlash) => paths
.Split(',', StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Trim().ToLowerInvariant())
.Where(x => x.IsNullOrWhiteSpace() == false)
.Select(reservedPath =>
{
var r = _hostingEnvironment.ToAbsolute(reservedPath).Trim().EnsureStartsWith('/');
return ensureTrailingSlash
? r.EnsureEndsWith('/')
: r;
})
.Where(reservedPathTrimmed => reservedPathTrimmed.IsNullOrWhiteSpace() == false);
private bool MatchesEndpoint(string absPath)
{
// Borrowed from https://stackoverflow.com/a/59550580
// Return a collection of Microsoft.AspNetCore.Http.Endpoint instances.
IEnumerable<RouteEndpoint> routeEndpoints = _endpointDataSource?.Endpoints.Cast<RouteEndpoint>();
var routeValues = new RouteValueDictionary();
// string localPath = new Uri(absPath).LocalPath;
// To get the matchedEndpoint of the provide url
RouteEndpoint matchedEndpoint = routeEndpoints
.Where(e => new TemplateMatcher(
TemplateParser.Parse(e.RoutePattern.RawText),
new RouteValueDictionary())
.TryMatch(absPath, routeValues))
.OrderBy(c => c.Order)
.FirstOrDefault();
return matchedEndpoint != null;
}
}
}

View File

@@ -6,6 +6,7 @@ using Umbraco.Core.DependencyInjection;
using Umbraco.Extensions;
using Umbraco.Infrastructure.DependencyInjection;
using Umbraco.Infrastructure.PublishedCache.DependencyInjection;
using Umbraco.Web.Common.Routing;
using Umbraco.Web.Website.Collections;
using Umbraco.Web.Website.Controllers;
using Umbraco.Web.Website.Routing;
@@ -40,6 +41,7 @@ namespace Umbraco.Web.Website.DependencyInjection
builder.Services.AddSingleton<HijackedRouteEvaluator>();
builder.Services.AddSingleton<IUmbracoRouteValuesFactory, UmbracoRouteValuesFactory>();
builder.Services.AddSingleton<IUmbracoRenderingDefaults, UmbracoRenderingDefaults>();
builder.Services.AddSingleton<RoutableDocumentFilter>();
builder.AddDistributedCache();

View File

@@ -1,210 +0,0 @@
using System;
using System.IO;
using System.Web;
using System.Web.Routing;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using System.Threading;
using System.Collections.Generic;
using System.Linq;
using System.Collections.Concurrent;
using Umbraco.Core.Collections;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.IO;
using Umbraco.Web.Composing;
namespace Umbraco.Web
{
/// <summary>
/// Utility class used to check if the current request is for a front-end request
/// </summary>
/// <remarks>
/// There are various checks to determine if this is a front-end request such as checking if the request is part of any reserved paths or existing MVC routes.
/// </remarks>
public sealed class RoutableDocumentFilter
{
public RoutableDocumentFilter(GlobalSettings globalSettings, IIOHelper ioHelper)
{
_globalSettings = globalSettings;
_ioHelper = ioHelper;
}
private static readonly ConcurrentDictionary<string, bool> RouteChecks = new ConcurrentDictionary<string, bool>();
private readonly GlobalSettings _globalSettings;
private readonly IIOHelper _ioHelper;
private object _locker = new object();
private bool _isInit = false;
private int? _routeCount;
private HashSet<string> _reservedList;
/// <summary>
/// Checks if the request is a document request (i.e. one that the module should handle)
/// </summary>
/// <param name="httpContext"></param>
/// <param name="uri"></param>
/// <returns></returns>
public bool IsDocumentRequest(HttpContextBase httpContext, Uri uri)
{
var maybeDoc = true;
var lpath = uri.AbsolutePath.ToLowerInvariant();
// handle directory-URLs used for asmx
// TODO: legacy - what's the point really?
var asmxPos = lpath.IndexOf(".asmx/", StringComparison.OrdinalIgnoreCase);
if (asmxPos >= 0)
{
// use uri.AbsolutePath, not path, 'cos path has been lowercased
httpContext.RewritePath(uri.AbsolutePath.Substring(0, asmxPos + 5), // filePath
uri.AbsolutePath.Substring(asmxPos + 5), // pathInfo
uri.Query.TrimStart('?'));
maybeDoc = false;
}
// a document request should be
// /foo/bar/nil
// /foo/bar/nil/
// /foo/bar/nil.aspx
// where /foo is not a reserved path
// if the path contains an extension that is not .aspx
// then it cannot be a document request
var extension = Path.GetExtension(lpath);
if (maybeDoc && extension.IsNullOrWhiteSpace() == false && extension != ".aspx")
maybeDoc = false;
// at that point, either we have no extension, or it is .aspx
// if the path is reserved then it cannot be a document request
if (maybeDoc && IsReservedPathOrUrl(lpath, httpContext, RouteTable.Routes))
maybeDoc = false;
//NOTE: No need to warn, plus if we do we should log the document, as this message doesn't really tell us anything :)
//if (!maybeDoc)
//{
// Logger.LogWarning<UmbracoModule>("Not a document");
//}
return maybeDoc;
}
/// <summary>
/// Determines whether the specified URL is reserved or is inside a reserved path.
/// </summary>
/// <param name="url">The URL to check.</param>
/// <returns>
/// <c>true</c> if the specified URL is reserved; otherwise, <c>false</c>.
/// </returns>
internal bool IsReservedPathOrUrl(string url)
{
LazyInitializer.EnsureInitialized(ref _reservedList, ref _isInit, ref _locker, () =>
{
// store references to strings to determine changes
var reservedPathsCache = _globalSettings.ReservedPaths;
var reservedUrlsCache = _globalSettings.ReservedUrls;
// add URLs and paths to a new list
var newReservedList = new HashSet<string>();
foreach (var reservedUrlTrimmed in reservedUrlsCache
.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Trim().ToLowerInvariant())
.Where(x => x.IsNullOrWhiteSpace() == false)
.Select(reservedUrl => _ioHelper.ResolveUrl(reservedUrl).Trim().EnsureStartsWith("/"))
.Where(reservedUrlTrimmed => reservedUrlTrimmed.IsNullOrWhiteSpace() == false))
{
newReservedList.Add(reservedUrlTrimmed);
}
foreach (var reservedPathTrimmed in NormalizePaths(reservedPathsCache.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)))
{
newReservedList.Add(reservedPathTrimmed);
}
foreach (var reservedPathTrimmed in NormalizePaths(ReservedPaths))
{
newReservedList.Add(reservedPathTrimmed);
}
// use the new list from now on
return newReservedList;
});
//The URL should be cleaned up before checking:
// * If it doesn't contain an '.' in the path then we assume it is a path based URL, if that is the case we should add an trailing '/' because all of our reservedPaths use a trailing '/'
// * We shouldn't be comparing the query at all
var pathPart = url.Split(new[] { '?' }, StringSplitOptions.RemoveEmptyEntries)[0].ToLowerInvariant();
if (pathPart.Contains(".") == false)
{
pathPart = pathPart.EnsureEndsWith('/');
}
// return true if URL starts with an element of the reserved list
return _reservedList.Any(x => pathPart.InvariantStartsWith(x));
}
private IEnumerable<string> NormalizePaths(IEnumerable<string> paths)
{
return paths
.Select(x => x.Trim().ToLowerInvariant())
.Where(x => x.IsNullOrWhiteSpace() == false)
.Select(reservedPath => _ioHelper.ResolveUrl(reservedPath).Trim().EnsureStartsWith("/").EnsureEndsWith("/"))
.Where(reservedPathTrimmed => reservedPathTrimmed.IsNullOrWhiteSpace() == false);
}
/// <summary>
/// Determines whether the current request is reserved based on the route table and
/// whether the specified URL is reserved or is inside a reserved path.
/// </summary>
/// <param name="url"></param>
/// <param name="httpContext"></param>
/// <param name="routes">The route collection to lookup the request in</param>
/// <returns></returns>
internal bool IsReservedPathOrUrl(string url, HttpContextBase httpContext, RouteCollection routes)
{
if (httpContext == null) throw new ArgumentNullException(nameof(httpContext));
if (routes == null) throw new ArgumentNullException(nameof(routes));
//This is some rudimentary code to check if the route table has changed at runtime, we're basically just keeping a count
//of the routes. This isn't fail safe but there's no way to monitor changes to the route table. Else we need to create a hash
//of all routes and then recompare but that will be annoying to do on each request and then we might as well just do the whole MVC
//route on each request like we were doing before instead of caching the result of GetRouteData.
var changed = false;
using (routes.GetReadLock())
{
if (!_routeCount.HasValue || _routeCount.Value != routes.Count)
{
//the counts are not set or have changed, need to reset
changed = true;
}
}
if (changed)
{
using (routes.GetWriteLock())
{
_routeCount = routes.Count;
//try clearing each entry
foreach(var r in RouteChecks.Keys.ToList())
RouteChecks.TryRemove(r, out _);
}
}
var absPath = httpContext?.Request?.Url.AbsolutePath;
if (absPath.IsNullOrWhiteSpace())
return false;
//check if the current request matches a route, if so then it is reserved.
var hasRoute = RouteChecks.GetOrAdd(absPath, x => routes.GetRouteData(httpContext) != null);
if (hasRoute)
return true;
//continue with the standard ignore routine
return IsReservedPathOrUrl(url);
}
/// <summary>
/// This is used internally to track any registered callback paths for Identity providers. If the request path matches
/// any of the registered paths, then the module will let the request keep executing
/// </summary>
internal static readonly ConcurrentHashSet<string> ReservedPaths = new ConcurrentHashSet<string>();
}
}

View File

@@ -46,8 +46,6 @@ namespace Umbraco.Web.Runtime
return new UmbracoHelper();
});
builder.Services.AddUnique<RoutableDocumentFilter>();
// configure the container for web
//composition.ConfigureForWeb();

View File

@@ -150,7 +150,6 @@
<Compile Include="AspNet\AspNetHttpContextAccessor.cs" />
<Compile Include="AspNet\AspNetIpResolver.cs" />
<Compile Include="AspNet\AspNetPasswordHasher.cs" />
<Compile Include="RoutableDocumentFilter.cs" />
<Compile Include="Runtime\AspNetUmbracoBootPermissionChecker.cs" />
<Compile Include="Security\MembershipProviderBase.cs" />
<Compile Include="Security\MembershipProviderExtensions.cs" />

View File

@@ -36,7 +36,6 @@ namespace Umbraco.Web
private readonly ILogger _logger;
private readonly IPublishedRouter _publishedRouter;
private readonly IUmbracoContextFactory _umbracoContextFactory;
private readonly RoutableDocumentFilter _routableDocumentLookup;
private readonly GlobalSettings _globalSettings;
private readonly IHostingEnvironment _hostingEnvironment;
@@ -45,7 +44,6 @@ namespace Umbraco.Web
ILogger logger,
IPublishedRouter publishedRouter,
IUmbracoContextFactory umbracoContextFactory,
RoutableDocumentFilter routableDocumentLookup,
GlobalSettings globalSettings,
IHostingEnvironment hostingEnvironment)
{
@@ -53,7 +51,6 @@ namespace Umbraco.Web
_logger = logger;
_publishedRouter = publishedRouter;
_umbracoContextFactory = umbracoContextFactory;
_routableDocumentLookup = routableDocumentLookup;
_globalSettings = globalSettings;
_hostingEnvironment = hostingEnvironment;
}
@@ -157,14 +154,15 @@ namespace Umbraco.Web
var reason = EnsureRoutableOutcome.IsRoutable;
// ensure this is a document request
if (!_routableDocumentLookup.IsDocumentRequest(httpContext, context.OriginalRequestUrl))
{
reason = EnsureRoutableOutcome.NotDocumentRequest;
}
//// ensure this is a document request
//if (!_routableDocumentLookup.IsDocumentRequest(httpContext, context.OriginalRequestUrl))
//{
// reason = EnsureRoutableOutcome.NotDocumentRequest;
//}
// ensure the runtime is in the proper state
// and deal with needed redirects, etc
else if (!EnsureRuntime(httpContext, uri))
if (!EnsureRuntime(httpContext, uri))
{
reason = EnsureRoutableOutcome.NotReady;
}