diff --git a/src/Umbraco.Web/RoutableDocumentLookup.cs b/src/Umbraco.Web/RoutableDocumentLookup.cs
new file mode 100644
index 0000000000..bc75798b09
--- /dev/null
+++ b/src/Umbraco.Web/RoutableDocumentLookup.cs
@@ -0,0 +1,109 @@
+using System;
+using System.IO;
+using System.Web;
+using System.Web.Routing;
+using Umbraco.Core;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Configuration;
+
+namespace Umbraco.Web
+{
+ ///
+ /// Utility class used to check if the current request is for a front-end request
+ ///
+ ///
+ /// 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.
+ ///
+ public sealed class RoutableDocumentLookup
+ {
+ public RoutableDocumentLookup(ILogger logger, IGlobalSettings globalSettings)
+ {
+ _logger = logger;
+ _globalSettings = globalSettings;
+ _combinedRouteCollection = new Lazy(CreateRouteCollection);
+ }
+
+ ///
+ /// Checks if the request is a document request (i.e. one that the module should handle)
+ ///
+ ///
+ ///
+ ///
+ 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 && _globalSettings.IsReservedPathOrUrl(lpath, httpContext, _combinedRouteCollection.Value))
+ 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.Warn("Not a document");
+ //}
+ return maybeDoc;
+ }
+
+ ///
+ /// This is used to be passed into the GlobalSettings.IsReservedPathOrUrl and will include some 'fake' routes
+ /// used to determine if a path is reserved.
+ ///
+ ///
+ /// This is basically used to reserve paths dynamically
+ ///
+ private readonly Lazy _combinedRouteCollection;
+ private readonly ILogger _logger;
+ private readonly IGlobalSettings _globalSettings;
+
+ private RouteCollection CreateRouteCollection()
+ {
+ var routes = new RouteCollection();
+
+ foreach (var route in RouteTable.Routes)
+ routes.Add(route);
+
+ foreach (var reservedPath in UmbracoModule.ReservedPaths)
+ {
+ try
+ {
+ routes.Add("_umbreserved_" + reservedPath.ReplaceNonAlphanumericChars(""),
+ new Route(reservedPath.TrimStart('/'), new StopRoutingHandler()));
+ }
+ catch (Exception ex)
+ {
+ _logger.Error("Could not add reserved path route", ex);
+ }
+ }
+
+ return routes;
+ }
+ }
+}
diff --git a/src/Umbraco.Web/Runtime/WebInitialComposer.cs b/src/Umbraco.Web/Runtime/WebInitialComposer.cs
index 4b4fb488e5..7c353daa1d 100644
--- a/src/Umbraco.Web/Runtime/WebInitialComposer.cs
+++ b/src/Umbraco.Web/Runtime/WebInitialComposer.cs
@@ -126,6 +126,7 @@ namespace Umbraco.Web.Runtime
composition.RegisterUnique(f => new DistributedCache());
composition.RegisterUnique();
+ composition.RegisterUnique();
// replace some services
composition.RegisterUnique();
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index 1a23ae7f65..2cdff55e2d 100755
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -230,6 +230,7 @@
+
diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs
index e4a921375e..95a96be072 100644
--- a/src/Umbraco.Web/UmbracoContext.cs
+++ b/src/Umbraco.Web/UmbracoContext.cs
@@ -1,16 +1,21 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Web;
+using System.Web.Routing;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
+using Umbraco.Core.Events;
using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Web;
using Umbraco.Web.PublishedCache;
using Umbraco.Web.Routing;
using Umbraco.Web.Security;
namespace Umbraco.Web
{
+
///
/// Class that encapsulates Umbraco information of a specific HTTP request
///
@@ -80,7 +85,7 @@ namespace Umbraco.Web
///
/// Raised when the published snapshot is being created
///
- internal event EventHandler CreatingPublishedSnapshot;
+ internal event TypedEventHandler CreatingPublishedSnapshot;
///
/// This is used internally for performance calculations, the ObjectCreated DateTime is set as soon as this
@@ -295,6 +300,23 @@ namespace Umbraco.Web
_previewing = _previewToken.IsNullOrWhiteSpace() == false;
}
+ private bool? _isDocumentRequest;
+
+ ///
+ /// Checks if the request is a document request (i.e. one that the module should handle)
+ ///
+ ///
+ ///
+ ///
+ internal bool IsDocumentRequest(RoutableDocumentLookup docLookup)
+ {
+ if (_isDocumentRequest.HasValue)
+ return _isDocumentRequest.Value;
+
+ _isDocumentRequest = docLookup.IsDocumentRequest(HttpContext, OriginalRequestUrl);
+ return _isDocumentRequest.Value;
+ }
+
// say we render a macro or RTE in a give 'preview' mode that might not be the 'current' one,
// then due to the way it all works at the moment, the 'current' published snapshot need to be in the proper
// default 'preview' mode - somehow we have to force it. and that could be recursive.
diff --git a/src/Umbraco.Web/UmbracoInjectedModule.cs b/src/Umbraco.Web/UmbracoInjectedModule.cs
index cbeee34444..70d9222dfc 100644
--- a/src/Umbraco.Web/UmbracoInjectedModule.cs
+++ b/src/Umbraco.Web/UmbracoInjectedModule.cs
@@ -39,6 +39,7 @@ namespace Umbraco.Web
private readonly IPublishedRouter _publishedRouter;
private readonly IUmbracoContextFactory _umbracoContextFactory;
private readonly BackgroundPublishedSnapshotNotifier _backgroundNotifier;
+ private readonly RoutableDocumentLookup _routableDocumentLookup;
public UmbracoInjectedModule(
IGlobalSettings globalSettings,
@@ -46,16 +47,16 @@ namespace Umbraco.Web
ILogger logger,
IPublishedRouter publishedRouter,
IUmbracoContextFactory umbracoContextFactory,
- BackgroundPublishedSnapshotNotifier backgroundNotifier)
+ BackgroundPublishedSnapshotNotifier backgroundNotifier,
+ RoutableDocumentLookup routableDocumentLookup)
{
- _combinedRouteCollection = new Lazy(CreateRouteCollection);
-
_globalSettings = globalSettings;
_runtime = runtime;
_logger = logger;
_publishedRouter = publishedRouter;
_umbracoContextFactory = umbracoContextFactory;
_backgroundNotifier = backgroundNotifier;
+ _routableDocumentLookup = routableDocumentLookup;
}
#region HttpModule event handlers
@@ -91,13 +92,16 @@ namespace Umbraco.Web
///
///
///
- private void UmbracoContext_CreatingPublishedSnapshot(object sender, EventArgs e)
+ private void UmbracoContext_CreatingPublishedSnapshot(UmbracoContext sender, EventArgs e)
{
// Wait for the notifier to complete if it's in progress, this is required because
// Pure Live models along with content snapshots are updated on a background thread when schema
// (doc types, data types) are changed so if that is still processing we need to wait before we access
// the content snapshot to make sure it's the latest version.
- if (_backgroundNotifier.Wait())
+ // We only want to wait if this is a front-end request (not a back office request) so need to first check
+ // for that and then wait.
+
+ if (sender.IsDocumentRequest(_routableDocumentLookup) &&_backgroundNotifier.Wait())
{
_logger.Debug("Request was suspended while waiting for background cache notifications to complete");
}
@@ -183,18 +187,18 @@ namespace Umbraco.Web
var reason = EnsureRoutableOutcome.IsRoutable;
// ensure this is a document request
- if (EnsureDocumentRequest(httpContext, uri) == false)
+ if (!context.IsDocumentRequest(_routableDocumentLookup))
{
reason = EnsureRoutableOutcome.NotDocumentRequest;
}
// ensure the runtime is in the proper state
// and deal with needed redirects, etc
- else if (EnsureRuntime(httpContext, uri) == false)
+ else if (!EnsureRuntime(httpContext, uri))
{
reason = EnsureRoutableOutcome.NotReady;
}
// ensure Umbraco has documents to serve
- else if (EnsureHasContent(context, httpContext) == false)
+ else if (!EnsureHasContent(context, httpContext))
{
reason = EnsureRoutableOutcome.NoContent;
}
@@ -202,55 +206,7 @@ namespace Umbraco.Web
return Attempt.If(reason == EnsureRoutableOutcome.IsRoutable, reason);
}
- ///
- /// Ensures that the request is a document request (i.e. one that the module should handle)
- ///
- ///
- ///
- ///
- private bool EnsureDocumentRequest(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 && _globalSettings.IsReservedPathOrUrl(lpath, httpContext, _combinedRouteCollection.Value))
- 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.Warn("Not a document");
- //}
-
- return maybeDoc;
- }
+
private bool EnsureRuntime(HttpContextBase httpContext, Uri uri)
{
@@ -506,36 +462,6 @@ namespace Umbraco.Web
#endregion
- ///
- /// This is used to be passed into the GlobalSettings.IsReservedPathOrUrl and will include some 'fake' routes
- /// used to determine if a path is reserved.
- ///
- ///
- /// This is basically used to reserve paths dynamically
- ///
- private readonly Lazy _combinedRouteCollection;
-
- private RouteCollection CreateRouteCollection()
- {
- var routes = new RouteCollection();
-
- foreach (var route in RouteTable.Routes)
- routes.Add(route);
-
- foreach (var reservedPath in UmbracoModule.ReservedPaths)
- {
- try
- {
- routes.Add("_umbreserved_" + reservedPath.ReplaceNonAlphanumericChars(""),
- new Route(reservedPath.TrimStart('/'), new StopRoutingHandler()));
- }
- catch (Exception ex)
- {
- _logger.Error("Could not add reserved path route", ex);
- }
- }
-
- return routes;
- }
+
}
}